RFC 7038914: VM could throw uncaught OOME in ReferenceHandler thread (original) (raw)
Peter Levart peter.levart at gmail.com
Fri May 10 13:15:12 UTC 2013
- Previous message: RFC 7038914: VM could throw uncaught OOME in ReferenceHandler thread
- Next message: RFC 7038914: VM could throw uncaught OOME in ReferenceHandler thread
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]
Hi again,
While the below patch works and keeps Reference Handler running in all scenarios, the evaluation exposed a weakness of JVM. If loading of a class fails because of OOME, this class can not be used in this incarnation of the VM even if OOME was just a transient condition.
Regards, Peter
On 05/10/2013 01:21 PM, Peter Levart wrote:
On 05/10/2013 12:52 PM, Peter Levart wrote:
While executing the above test with the patch to ReferenceHandler applied, I noticed a strange behaviour. I can reproduce this behaviour reliably on both JDK7 and JDK8. When the patch is applied as proposed:
try { lock.wait(); } catch (InterruptedException | OutOfMemoryError x) { } ... I still get the following output from the test (reliably, always): Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "Reference Handler" Exception in thread "main" java.lang.Exception: Reference Handler thread died. at OOMEInReferenceHandler.main(OOMEInReferenceHandler.java:80) But when i change the patch to the following: try { lock.wait(); } catch (OutOfMemoryError | InterruptedException x) { } ...the test reliably and always passes. My explanation to his behaviour is that the order of exception handlers changes the order of class referencing. In the former variation (that still throws OOME) the following seems to be happening: wait() is interrupted and InterruptedException instance creation is attempted. Because this is the 1st reference to InterruptedException class in the lifetime of the JVM, loading of InterruptedException class is attempted which fails because of OOME. This OOME is caught by handler and ignored. But after handling of this OOME, another reference to InterruptedException class is attempted by exception handlers themselves (I don't know how exception handlers work exactly, but I have a feeling this is happening). Because InterruptedException class was not successfully loaded the 1st time tried, every reference to this class must throw NoClassDefFoundError, so this is attempted, but creation of NoClassDefFoundError fails because there's no heap space and another OOME is thrown - this time out of exception handling block, which is propagated and kills the thread. If the order of exception handlers is reversed, this second OOME is caught and ignored. Hi, This really seems to be happening (at least approximately, see below) because if InterruptedException class is preloaded at start of test, the order of exception handling does not have any impact on test. By disassembling the class-files of both variants, I found the only difference is the order of OutOfMemoryError & InterruptedException entries found in exception table: catch (InterruptedException | OutOfMemoryError x) variant has: public void run(); Code: 0: invokestatic #2 // Method java/lang/ref/Reference.access$100:()Ljava/lang/ref/Reference$Lock; 3: dup 4: astore2 5: monitorenter 6: invokestatic #3 // Method java/lang/ref/Reference.access$200:()Ljava/lang/ref/Reference; 9: ifnull 33 12: invokestatic #3 // Method java/lang/ref/Reference.access$200:()Ljava/lang/ref/Reference; 15: astore1 16: aload1 17: invokestatic #4 // Method java/lang/ref/Reference.access$300:(Ljava/lang/ref/Reference;)Ljava/lang/ref/Reference; 20: invokestatic #5 // Method java/lang/ref/Reference.access$202:(Ljava/lang/ref/Reference;)Ljava/lang/ref/Reference; 23: pop 24: aload1 25: aconstnull 26: invokestatic #6 // Method java/lang/ref/Reference.access$302:(Ljava/lang/ref/Reference;Ljava/lang/ref/Reference;)Ljava/lang/ref/Reference; 29: pop 30: goto 48 * 33: invokestatic #2 // Method java/lang/ref/Reference.access$100:()Ljava/lang/ref/Reference$Lock;** ** 36: invokevirtual #7 // Method java/lang/Object.wait:()V** ** 39: goto 43* 42: astore3 43: aload2 44: monitorexit 45: goto 0 48: aload2 49: monitorexit 50: goto 60 53: astore 4 55: aload2 56: monitorexit 57: aload 4 59: athrow 60: aload1 61: instanceof #10 // class sun/misc/Cleaner 64: ifeq 77 67: aload1 68: checkcast #10 // class sun/misc/Cleaner 71: invokevirtual #11 // Method sun/misc/Cleaner.clean:()V 74: goto 0 77: aload1 78: getfield #12 // Field java/lang/ref/Reference.queue:Ljava/lang/ref/ReferenceQueue; 81: astore2 82: aload2 83: getstatic #13 // Field java/lang/ref/ReferenceQueue.NULL:Ljava/lang/ref/ReferenceQueue; 86: ifacmpeq 95 89: aload2 90: aload1 91: invokevirtual #14 // Method java/lang/ref/ReferenceQueue.enqueue:(Ljava/lang/ref/Reference;)Z 94: pop 95: goto 0 Exception table: from to target type * 33 39 42 Class java/lang/InterruptedException** ** 33 39 42 Class java/lang/OutOfMemoryError* 6 45 53 any 48 50 53 any 53 57 53 any catch (OutOfMemoryError | InterruptedException x) variant has the exactly same bytecodes but the following exception table: Exception table: from to target type * 33 39 42 Class java/lang/OutOfMemoryError** ** 33 39 42 Class java/lang/InterruptedException* 6 45 53 any 48 50 53 any 53 57 53 any ... so what seems to be happening is a little different but similar to what I have explained. In the former variant (that still throws OOME), the handler 1st checks for the type of thrown exception against InterruptedException class, which fails and attempts to throw NoClassDefFoundError which can't be allocated so another OOME is thrown, but in the later variant the 1st check is against OutOfMemoryError class which succeeds, so the empty handler is executed and no more checks are made (no 2nd reference to InterruptedException class). The fix I proposed in previous mail works (OOME is thrown twice and 2nd OOME is handled), but also the following would work (if the order of checks follows the source in every compiler): try { lock.wait(); } catch (OutOfMemoryError x) { } catch (InterruptedException x) { } ...the benefit of this is that OOME is never thrown two times. Regards, Peter
- Previous message: RFC 7038914: VM could throw uncaught OOME in ReferenceHandler thread
- Next message: RFC 7038914: VM could throw uncaught OOME in ReferenceHandler thread
- Messages sorted by: [ date ] [ thread ] [ subject ] [ author ]