Hi Vladimir, Peter, once more, thanks for all your comments. The revised webrev is at http://cr.openjdk.java.net/~mhaupt/8131129/webrev.01/.
Best, Michael > Am 28.10.2015 um 17:37 schrieb Vladimir Ivanov <vladimir.x.iva...@oracle.com>: > > Peter, > >> Hi Vladimir, I think you are right. The proposed fix alone even >> increases the chance of duplicate class definition attempts as it delays >> registration in cache to after whole clinit successfully finishes. I >> would still keep this initialization rearrangement as it fixes the >> possible publication race too. If the source of 1st exception is from >> clinit method execution, then it would be best to skip forced class >> initialization and delay it to its 1st use. Eager initialization does >> not serve any purpose if the registration into cache is performed out of >> clinit method as proposed in the fix. >> >> To summarise: the proposed initialization fix would have to be >> accompanied with removal of forced class initialization to achieve the >> effect of your option (1) below. > There's no benefit from skipping eager initialization - if class > initialization fails, the class becomes unusable anyway. > > The idea was to split class init and SPECIES_DATA field init. It would allow > to retry Species_* class init later once the initial attempt failed. But > there's not much value is that right now, since VM doesn't unload generated > adapters. So, once code cache is exhausted there's not much JVM can do about > it. > > Also, publishing not fully initialized Species_* class requires all users to > check whether the initialization is completed, which can be too much from > performance perspective. > > So, I'm in favor of modified (3) Michael proposed. > > Best regards, > Vladimir Ivanov > >> 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) >> ----- >> >> >> -- <http://www.oracle.com/> Dr. Michael Haupt | Principal Member of Technical Staff Phone: +49 331 200 7277 | Fax: +49 331 200 7561 Oracle Java Platform Group | LangTools Team | Nashorn Oracle Deutschland B.V. & Co. KG, Schiffbauergasse 14 | 14467 Potsdam, Germany <http://www.oracle.com/commitment> Oracle is committed to developing practices and products that help protect the environment