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()));