Michael,

thank you. Of course you're right: I'll dub this issue "Hydra" - one head 
chopped off, at least one missed, new ones growing. At least the understanding improves 
in each iteration ... ;-)

The proposal as it now is indeed has the risk of leaving a stale cache entry 
(placeholder) if species loading or initialisation break. The other solution 
that was proposed would have wrapped the class loading and initialisation step 
in a try/catch block and rolled back the cache entry for the types string at 
hand in case anything went wrong. This, too, could lead to the duplicate 
definition error if the same species is requested again.

At the foundation of the problem, there's a code cache that overflows. This is 
inside the VM, and, as you notice, not easily addressed. In fact, if the 
application creates so many adapters that the VM cache overflows, it suggests a 
problem with the application. It's like forcing an OOME by just going mad on 
allocations. Thus, I don't see how (1) would help.
(1) has a nice property of throwing the same error again (VirtualMachineError: "out of space in CodeCache for method handle intrinsic") on repeated attempts.

But it also complicates the implementation, so I'm in favor of your proposal.

I agree that this is a test problem that has never occurred "in the wild". I 
also agree that, in case it would, the error message should be clearer.

Proposal - this is a derivative of (3):
* Keep the fix as it is, because it certainly clarifies the cache 
initialisation logic.
Agree. The cleanup changes are good.

* Additionally, harden the class loading / initialisation step:
   - Wrap it in a try/catch block.
   - In case something goes wrong, mark the species as failure in the cache and 
raise an appropriate error.
* Also, check for failed species upon retrieval and raise an appropriate error.
Sounds good.

* Finally, harden the test itself against such errors, much like it deals with 
code cache overflows.
I'd like to avoid introducing another expected failing mode and would prefer the implementation to throw the same type of error - VirtualMachineError. In that case there should be no need to adjust the test.

Best regards,
Vladimir Ivanov


How about it?

Best,

Michael

Am 28.10.2015 um 13:02 schrieb Vladimir Ivanov <vladimir.x.iva...@oracle.com>:

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




Reply via email to