8139270: Drastically reduce memory footprint of ChainedCallSite

Reviewed-by: hannesw, sundar
This commit is contained in:
Attila Szegedi 2015-10-12 13:44:38 +02:00
parent 53051d4e34
commit 89fa209a47

View File

@ -85,9 +85,9 @@ package jdk.internal.dynalink;
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 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
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 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
}
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 @@ public class ChainedCallSite extends AbstractRelinkableCallSite {
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;
}