8139270: Drastically reduce memory footprint of ChainedCallSite
Reviewed-by: hannesw, sundar
--- 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;
}