8139270: Drastically reduce memory footprint of ChainedCallSite
authorattila
Mon, 12 Oct 2015 13:44:38 +0200
changeset 33006 99298bc38e28
parent 33005 19c2fe67d821
child 33007 03119bfefbbf
8139270: Drastically reduce memory footprint of ChainedCallSite Reviewed-by: hannesw, sundar
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/ChainedCallSite.java
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/ChainedCallSite.java	Mon Oct 12 13:36:41 2015 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/internal/dynalink/ChainedCallSite.java	Mon Oct 12 13:44:38 2015 +0200
@@ -85,9 +85,9 @@
 
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
+import java.util.Arrays;
 import java.util.Iterator;
 import java.util.LinkedList;
-import java.util.concurrent.atomic.AtomicReference;
 import jdk.internal.dynalink.linker.GuardedInvocation;
 import jdk.internal.dynalink.support.AbstractRelinkableCallSite;
 import jdk.internal.dynalink.support.Lookup;
@@ -112,7 +112,14 @@
         PRUNE_SWITCHPOINTS = MethodHandles.insertArguments(PRUNE, 2, false);
     }
 
-    private final AtomicReference<LinkedList<GuardedInvocation>> invocations = new AtomicReference<>();
+    /**
+     * Contains the invocations currently linked into this call site's target. They are used when we are
+     * relinking to rebuild the guardWithTest chain. Valid values for this field are: {@code null} if there's
+     * no linked invocations, or an instance of {@link GuardedInvocation} if there is exactly one previous
+     * invocation, or an instance of {@code GuardedInvocation[]} if there is more than one previous
+     * invocation.
+     */
+    private Object invocations;
 
     /**
      * Creates a new chained call site.
@@ -142,10 +149,18 @@
     }
 
     private MethodHandle relinkInternal(final GuardedInvocation invocation, final MethodHandle relink, final boolean reset, final boolean removeCatches) {
-        final LinkedList<GuardedInvocation> currentInvocations = invocations.get();
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        final LinkedList<GuardedInvocation> newInvocations =
-            currentInvocations == null || reset ? new LinkedList<>() : (LinkedList)currentInvocations.clone();
+        final Object currentInvocations = invocations;
+        final LinkedList<GuardedInvocation> newInvocations;
+        if (currentInvocations == null || reset) {
+            newInvocations = new LinkedList<>();
+        } else if (currentInvocations instanceof GuardedInvocation) {
+            newInvocations = new LinkedList<>();
+            newInvocations.add((GuardedInvocation)currentInvocations);
+        } else if (currentInvocations instanceof GuardedInvocation[]) {
+            newInvocations = new LinkedList<>(Arrays.asList(((GuardedInvocation[])currentInvocations)));
+        } else {
+            throw new AssertionError();
+        }
 
         // First, prune the chain of invalidated switchpoints, we always do this
         // We also remove any catches if the remove catches flag is set
@@ -177,12 +192,17 @@
             target = inv.compose(target, pruneAndInvokeSwitchPoints, pruneAndInvokeCatches);
         }
 
-        // If nobody else updated the call site while we were rebuilding the chain, set the target to our chain. In case
-        // we lost the race for multithreaded update, just do nothing. Either the other thread installed the same thing
-        // we wanted to install, or otherwise, we'll be asked to relink again.
-        if(invocations.compareAndSet(currentInvocations, newInvocations)) {
-            setTarget(target);
+        switch (newInvocations.size()) {
+            case 0:
+                invocations = null;
+                break;
+            case 1:
+                invocations = newInvocations.getFirst();
+                break;
+            default:
+                invocations = newInvocations.toArray(new GuardedInvocation[newInvocations.size()]);
         }
+        setTarget(target);
         return target;
     }