You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by ta...@apache.org on 2026/07/07 21:12:12 UTC
(myfaces) branch main updated: [perf] reduce array instances
This is an automated email from the ASF dual-hosted git repository.
tandraschko pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces.git
The following commit(s) were added to refs/heads/main by this push:
new e7fb71692 [perf] reduce array instances
e7fb71692 is described below
commit e7fb716922027f0edfec8e93698c072f18a2a7f4
Author: tandraschko <ta...@apache.org>
AuthorDate: Tue Jul 7 23:12:01 2026 +0200
[perf] reduce array instances
---
.../jakarta/faces/component/UIComponentBase.java | 30 +++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/api/src/main/java/jakarta/faces/component/UIComponentBase.java b/api/src/main/java/jakarta/faces/component/UIComponentBase.java
index d4865ab7c..9347de7f5 100755
--- a/api/src/main/java/jakarta/faces/component/UIComponentBase.java
+++ b/api/src/main/java/jakarta/faces/component/UIComponentBase.java
@@ -54,6 +54,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import jakarta.faces.event.PhaseId;
import org.apache.myfaces.core.api.shared.lang.Assert;
@@ -84,6 +85,27 @@ public abstract class UIComponentBase extends UIComponent
private static final String _STRING_BUILDER_KEY
= "jakarta.faces.component.UIComponentBase.SHARED_STRING_BUILDER";
+ // perf: cache of zero-length FacesListener[] arrays keyed by the requested listener class,
+ // so getFacesListeners(Class) does not need to allocate a new array (via reflection) every
+ // time a component has no listeners of the requested type registered, which is the most
+ // common case (e.g. every getActionListeners()/getValueChangeListeners() call, and every
+ // broadcast() check). The array type still matches the requested class exactly (required
+ // because callers cast the result, e.g. to ActionListener[]), and since the arrays are
+ // zero-length there is nothing that could be mutated to leak state between callers.
+ private static final Map<Class<?>, FacesListener[]> EMPTY_FACES_LISTENER_ARRAYS
+ = new ConcurrentHashMap<>(8);
+
+ private static FacesListener[] emptyFacesListenerArray(Class<? extends FacesListener> clazz)
+ {
+ FacesListener[] array = EMPTY_FACES_LISTENER_ARRAYS.get(clazz);
+ if (array == null)
+ {
+ array = EMPTY_FACES_LISTENER_ARRAYS.computeIfAbsent(clazz,
+ key -> (FacesListener[]) Array.newInstance(key, 0));
+ }
+ return array;
+ }
+
// See ViewPoolProcessor for comments and usages
static final int RESET_MODE_OFF = 0;
static final int RESET_MODE_SOFT = 1;
@@ -1235,7 +1257,7 @@ protected FacesListener[] getFacesListeners(Class<? extends FacesListener> clazz
if (_facesListeners == null)
{
- return (FacesListener[]) Array.newInstance(clazz, 0);
+ return emptyFacesListenerArray(clazz);
}
List<FacesListener> lst = null;
@@ -1247,7 +1269,9 @@ protected FacesListener[] getFacesListeners(Class<? extends FacesListener> clazz
{
if (lst == null)
{
- lst = new ArrayList<>(5);
+ // lst can never hold more than "size" elements, so avoid over-allocating
+ // the backing array when the component has fewer than 5 listeners in total
+ lst = new ArrayList<>(Math.min(size, 5));
}
lst.add(facesListener);
}
@@ -1255,7 +1279,7 @@ protected FacesListener[] getFacesListeners(Class<? extends FacesListener> clazz
if (lst == null || lst.isEmpty())
{
- return (FacesListener[]) Array.newInstance(clazz, 0);
+ return emptyFacesListenerArray(clazz);
}
return lst.toArray((FacesListener[]) Array.newInstance(clazz, lst.size()));