Regards, Peter
Michael, Peter,
Thanks for detailed analysis! It was a hard one to track down :-)
Unfortunately, I don't see how the proposed change fixes the problem.
The failure occurs during SpeciesData instantiation
(BMH$SpeciesData.<init>). It means updateCache has not yet been called
from getForClass:
static SpeciesData getForClass(String types, Class<? extends
BoundMethodHandle> clazz) {
return updateCache(types, new SpeciesData(types, clazz));
}
With your changes, updateCache is called only after successful loading
of a class. So, the cache isn't updated if there was an error during
previous attempt.
The real problem is more severe IMO. The loaded class is useless when
its initialization finishes abruptly, so there's no way to instantiate
it or reload. Right now it is manifested as a duplicate class definition
attempt, but if you avoid repeated class loading it will just fail
slightly later - the system is already broken and there's no way to recover.
I see the following ways to proceed:
(1) delay class initialization (fill in Species_*.SPECIES_DATA field
afterwards) and attempt to finish initialization on subsequent requests,
but skipping class loading step;
(2) use VM anonymous classes (see JDK-8078602 [1]) and just retry
Species creation;
(3) give up on that particular Species shape and throw an error
whenever it is requested (how it works right now, but with more
meaningful error message).
I remember I experimented with (2), but even after JDK-8078629 [2] there
was still measurable peak performance regression on Octane/Nashorn.
(1) looks more favorable and robust, but right now there shouldn't be
much difference - there's no support in VM to unload MH.linkTo*
adapters, so the failure will repeatedly occur once the code cache is full.
My conclusion is that it is mostly a test problem, but java.lang.invoke
framework should clearly communicate the reason why it fails. Since the
test is aware about code cache overflow problems, it looks preferable to
go with (1) or (3) for now. VM should throw VirtualMachineError on
repeated attempts to instantiate SPECIES_DATA and the test already
filters them out.
Best regards,
Vladimir Ivanov
[1] https://bugs.openjdk.java.net/browse/JDK-8078602
[2] https://bugs.openjdk.java.net/browse/JDK-8078629
On 10/28/15 12:19 PM, Michael Haupt wrote:
Dear all,
please review this change.
Bug: https://bugs.openjdk.java.net/browse/JDK-8131129
Webrev: http://cr.openjdk.java.net/~mhaupt/8131129/webrev.00/
Thanks to Peter Levart, who has contributed the actual code to fix
the issue.
The background of this is as follows. Bug 8131129 is an intermittent
failure in a multi-threaded test for LambdaForm caching. The failure
was not reproducible but used to occur on various platforms every
now and then. I was able to dig up some stack traces that reveal the
real cause in internal test logs. These traces finally confirmed a
suspicion that both Peter and I had had but could, for lack of
complete failure information, not pin down as the cause of the
issue. (Most traces from failures of the test were only partial, as
the test harness is selective about traces it logs.)
At the heart of the problem is an overflowing code cache for
adapters in the VM. The test, dynamically generating considerable
amounts of LambdaForm and BoundMethodHandle species classes, would
eventually trigger this overflow. The place at which the problem
would surface is this, in
BoundMethodHandle.Factory.generateConcreteBMHClass():
Class<? extends BoundMethodHandle> bmhClass =
//UNSAFE.defineAnonymousClass(BoundMethodHandle.class,
classFile, null).asSubclass(BoundMethodHandle.class);
UNSAFE.defineClass(className, classFile, 0, classFile.length,
BoundMethodHandle.class.getClassLoader(), null)
.asSubclass(BoundMethodHandle.class);
UNSAFE.ensureClassInitialized(bmhClass);
The test would generate a BMH species with a given type signature
via defineClass and immediately thereafter force initialisation of
that class, in the course of which a SpeciesData instance is
supposed to be created and entered in the BMH species cache. In case
the VM's adapter code cache overflowed during this operation, the
BMH species' class initialiser would not finish its execution
properly, leading to a stale placeholder entry left behind in the
cache. The presence of this very placeholder would trick the next
request for a BMH species with the same type signature into
believing the class needed to be defined, leading to the observed
failure.
An exemplary stack trace is appended below.
Both Peter and I had come up with solutions, but I think Peter's is
better as it gets rid of the somewhat intricate and semi-hidden
cache registration call made from generated code. With Peter's fix,
it's all visible in library code now.
Thanks,
Michael
-----
java.lang.InternalError: java.lang.NoSuchMethodException: no such
method:
java.lang.invoke.MethodHandle.linkToStatic(Object,Object,int,Object,Object,Object,Object,Object,Object,Object,MemberName)Object/invokeStatic
at
java.lang.invoke.MethodHandleStatics.newInternalError(MethodHandleStatics.java:120)
at
java.lang.invoke.DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:214)
at
java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:188)
at
java.lang.invoke.DirectMethodHandle.preparedLambdaForm(DirectMethodHandle.java:177)
at
java.lang.invoke.DirectMethodHandle.make(DirectMethodHandle.java:84)
at
java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(MethodHandles.java:1655)
at
java.lang.invoke.MethodHandles$Lookup.getDirectMethod(MethodHandles.java:1600)
at
java.lang.invoke.MethodHandles$Lookup.findStatic(MethodHandles.java:777)
at
java.lang.invoke.BoundMethodHandle$Factory.makeCbmhCtor(BoundMethodHandle.java:817)
at
java.lang.invoke.BoundMethodHandle$Factory.makeCtors(BoundMethodHandle.java:772)
at
java.lang.invoke.BoundMethodHandle$SpeciesData.<init>(BoundMethodHandle.java:347)
at
java.lang.invoke.BoundMethodHandle$SpeciesData.getForClass(BoundMethodHandle.java:411)
at
java.lang.invoke.BoundMethodHandle$Species_IL7.<clinit>(Species_IL7)
at sun.misc.Unsafe.ensureClassInitialized(Native Method)
at
java.lang.invoke.BoundMethodHandle$Factory.generateConcreteBMHClass(BoundMethodHandle.java:718)
at
java.lang.invoke.BoundMethodHandle$SpeciesData.get(BoundMethodHandle.java:401)
at
java.lang.invoke.BoundMethodHandle$SpeciesData.extendWith(BoundMethodHandle.java:388)
at
java.lang.invoke.LambdaFormEditor.newSpeciesData(LambdaFormEditor.java:363)
at
java.lang.invoke.LambdaFormEditor.makeArgumentCombinationForm(LambdaFormEditor.java:631)
at
java.lang.invoke.LambdaFormEditor.filterArgumentForm(LambdaFormEditor.java:612)
at
java.lang.invoke.MethodHandles.filterArgument(MethodHandles.java:2667)
at
java.lang.invoke.MethodHandles.filterArguments(MethodHandles.java:2654)
at TestMethods$4.getMH(TestMethods.java:180)
at TestMethods.getTestCaseMH(TestMethods.java:548)
at
LFMultiThreadCachingTest.lambda$doTest$0(LFMultiThreadCachingTest.java:80)
at LFMultiThreadCachingTest$$Lambda$5/21979926.run(Unknown
Source)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NoSuchMethodException: no such method:
java.lang.invoke.MethodHandle.linkToStatic(Object,Object,int,Object,Object,Object,Object,Object,Object,Object,MemberName)Object/invokeStatic
at
java.lang.invoke.MemberName.makeAccessException(MemberName.java:873)
at
java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:990)
at
java.lang.invoke.DirectMethodHandle.makePreparedLambdaForm(DirectMethodHandle.java:212)
... 25 more
Caused by: java.lang.NoSuchMethodError:
java.lang.invoke.MethodHandle.linkToStatic(Ljava/lang/Object;Ljava/lang/Object;ILjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/invoke/MemberName;)Ljava/lang/Object;
at java.lang.invoke.MethodHandleNatives.resolve(Native Method)
at
java.lang.invoke.MemberName$Factory.resolve(MemberName.java:962)
at
java.lang.invoke.MemberName$Factory.resolveOrFail(MemberName.java:987)
... 26 more
Caused by: java.lang.VirtualMachineError: out of space in CodeCache
for method handle intrinsic
... 29 more
java.lang.LinkageError: loader (instance of <bootloader>):
attempted duplicate class definition for name:
"java/lang/invoke/BoundMethodHandle$Species_IL7"
at sun.misc.Unsafe.defineClass(Native Method)
at
java.lang.invoke.BoundMethodHandle$Factory.generateConcreteBMHClass(BoundMethodHandle.java:715)
at
java.lang.invoke.BoundMethodHandle$SpeciesData.get(BoundMethodHandle.java:401)
at
java.lang.invoke.BoundMethodHandle$SpeciesData.extendWith(BoundMethodHandle.java:388)
at
java.lang.invoke.LambdaFormEditor.newSpeciesData(LambdaFormEditor.java:363)
at
java.lang.invoke.LambdaFormEditor.makeArgumentCombinationForm(LambdaFormEditor.java:631)
at
java.lang.invoke.LambdaFormEditor.filterArgumentForm(LambdaFormEditor.java:612)
at
java.lang.invoke.MethodHandles.filterArgument(MethodHandles.java:2667)
at
java.lang.invoke.MethodHandles.filterArguments(MethodHandles.java:2654)
at TestMethods$4.getMH(TestMethods.java:180)
at TestMethods.getTestCaseMH(TestMethods.java:548)
at
LFMultiThreadCachingTest.lambda$doTest$0(LFMultiThreadCachingTest.java:80)
at LFMultiThreadCachingTest$$Lambda$5/21979926.run(Unknown
Source)
at java.lang.Thread.run(Thread.java:745)
java.lang.LinkageError: loader (instance of <bootloader>):
attempted duplicate class definition for name:
"java/lang/invoke/BoundMethodHandle$Species_IL7"
at sun.misc.Unsafe.defineClass(Native Method)
at
java.lang.invoke.BoundMethodHandle$Factory.generateConcreteBMHClass(BoundMethodHandle.java:715)
at
java.lang.invoke.BoundMethodHandle$SpeciesData.get(BoundMethodHandle.java:401)
at
java.lang.invoke.BoundMethodHandle$SpeciesData.extendWith(BoundMethodHandle.java:388)
at
java.lang.invoke.LambdaFormEditor.newSpeciesData(LambdaFormEditor.java:363)
at
java.lang.invoke.LambdaFormEditor.makeArgumentCombinationForm(LambdaFormEditor.java:631)
at
java.lang.invoke.LambdaFormEditor.filterArgumentForm(LambdaFormEditor.java:612)
at
java.lang.invoke.MethodHandles.filterArgument(MethodHandles.java:2667)
at
java.lang.invoke.MethodHandles.filterArguments(MethodHandles.java:2654)
at TestMethods$4.getMH(TestMethods.java:180)
at TestMethods.getTestCaseMH(TestMethods.java:548)
at
LFMultiThreadCachingTest.lambda$doTest$0(LFMultiThreadCachingTest.java:80)
at LFMultiThreadCachingTest$$Lambda$5/21979926.run(Unknown
Source)
at java.lang.Thread.run(Thread.java:745)
STATUS:Failed.STATUS:Failed.STATUS:Failed.`main' threw exception:
java.lang.LinkageError: loader (instance of <bootloader>): attempted
duplicate class definition for name:
"java/lang/invoke/BoundMethodHandle$Species_IL7"
`main' threw exception: java.lang.LinkageError: loader (instance of
<bootloader>): attempted duplicate class definition for name:
"java/lang/invoke/BoundMethodHandle$Species_IL7"
`main' threw exception: java.lang.InternalError:
java.lang.NoSuchMethodException: no such method:
java.lang.invoke.MethodHandle.linkToStatic(Object,Object,int,Object,Object,Object,Object,Object,Object,Object,MemberName)Object/invokeStatic
java.lang.LinkageError: loader (instance of <bootloader>):
attempted duplicate class definition for name:
"java/lang/invoke/BoundMethodHandle$Species_IL7"
at sun.misc.Unsafe.defineClass(Native Method)
at
java.lang.invoke.BoundMethodHandle$Factory.generateConcreteBMHClass(BoundMethodHandle.java:715)
at
java.lang.invoke.BoundMethodHandle$SpeciesData.get(BoundMethodHandle.java:401)
-----