<html>
  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <div class="moz-cite-prefix">On 05/10/2013 12:52 PM, Peter Levart
      wrote:<br>
    </div>
    <blockquote cite="mid:518CD15F.70503@gmail.com" type="cite">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:
      <br>
      <br>
                              try {
      <br>
                                  lock.wait();
      <br>
                              } catch (InterruptedException |
      OutOfMemoryError  x) { }
      <br>
      <br>
      ... I still get the following output from the test (reliably,
      always):
      <br>
      <br>
      Exception: java.lang.OutOfMemoryError thrown from the
      UncaughtExceptionHandler in thread "Reference Handler"
      <br>
      Exception in thread "main" java.lang.Exception: Reference Handler
      thread died.
      <br>
              at
      OOMEInReferenceHandler.main(OOMEInReferenceHandler.java:80)
      <br>
      <br>
      But when i change the patch to the following:
      <br>
      <br>
                              try {
      <br>
                                  lock.wait();
      <br>
                              } catch (OutOfMemoryError |
      InterruptedException  x) { }
      <br>
      <br>
      ...the test reliably and always passes.
      <br>
      <br>
      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:
      <br>
      <br>
      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.
      <br>
      <br>
      If the order of exception handlers is reversed, this second OOME
      is caught and ignored.
    </blockquote>
    <br>
    Hi,<br>
    <br>
    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.<br>
    <br>
    By disassembling the class-files of both variants, I found the only
    difference is the order of OutOfMemoryError &
    InterruptedException entries found in exception table:<br>
    <br>
    catch (InterruptedException | OutOfMemoryError  x) variant has:<br>
    <br>
    <tt>  public void run();</tt><tt><br>
    </tt><tt>    Code:</tt><tt><br>
    </tt><tt>       0: invokestatic  #2                  // Method
      java/lang/ref/Reference.access$100:()Ljava/lang/ref/Reference$Lock;</tt><tt><br>
    </tt><tt>       3: dup           </tt><tt><br>
    </tt><tt>       4: astore_2      </tt><tt><br>
    </tt><tt>       5: monitorenter  </tt><tt><br>
    </tt><tt>       6: invokestatic  #3                  // Method
      java/lang/ref/Reference.access$200:()Ljava/lang/ref/Reference;</tt><tt><br>
    </tt><tt>       9: ifnull        33</tt><tt><br>
    </tt><tt>      12: invokestatic  #3                  // Method
      java/lang/ref/Reference.access$200:()Ljava/lang/ref/Reference;</tt><tt><br>
    </tt><tt>      15: astore_1      </tt><tt><br>
    </tt><tt>      16: aload_1       </tt><tt><br>
    </tt><tt>      17: invokestatic  #4                  // Method
java/lang/ref/Reference.access$300:(Ljava/lang/ref/Reference;)Ljava/lang/ref/Reference;</tt><tt><br>
    </tt><tt>      20: invokestatic  #5                  // Method
java/lang/ref/Reference.access$202:(Ljava/lang/ref/Reference;)Ljava/lang/ref/Reference;</tt><tt><br>
    </tt><tt>      23: pop           </tt><tt><br>
    </tt><tt>      24: aload_1       </tt><tt><br>
    </tt><tt>      25: aconst_null   </tt><tt><br>
    </tt><tt>      26: invokestatic  #6                  // Method
java/lang/ref/Reference.access$302:(Ljava/lang/ref/Reference;Ljava/lang/ref/Reference;)Ljava/lang/ref/Reference;</tt><tt><br>
    </tt><tt>      29: pop           </tt><tt><br>
    </tt><tt>      30: goto          48</tt><tt><br>
    </tt><b><tt>      33: invokestatic  #2                  // Method
        java/lang/ref/Reference.access$100:()Ljava/lang/ref/Reference$Lock;</tt></b><b><tt><br>
      </tt></b><b><tt>      36: invokevirtual #7                  //
        Method java/lang/Object.wait:()V</tt></b><b><tt><br>
      </tt></b><b><tt>      39: goto          43</tt></b><tt><br>
    </tt><tt>      42: astore_3      </tt><tt><br>
    </tt><tt>      43: aload_2       </tt><tt><br>
    </tt><tt>      44: monitorexit   </tt><tt><br>
    </tt><tt>      45: goto          0</tt><tt><br>
    </tt><tt>      48: aload_2       </tt><tt><br>
    </tt><tt>      49: monitorexit   </tt><tt><br>
    </tt><tt>      50: goto          60</tt><tt><br>
    </tt><tt>      53: astore        4</tt><tt><br>
    </tt><tt>      55: aload_2       </tt><tt><br>
    </tt><tt>      56: monitorexit   </tt><tt><br>
    </tt><tt>      57: aload         4</tt><tt><br>
    </tt><tt>      59: athrow        </tt><tt><br>
    </tt><tt>      60: aload_1       </tt><tt><br>
    </tt><tt>      61: instanceof    #10                 // class
      sun/misc/Cleaner</tt><tt><br>
    </tt><tt>      64: ifeq          77</tt><tt><br>
    </tt><tt>      67: aload_1       </tt><tt><br>
    </tt><tt>      68: checkcast     #10                 // class
      sun/misc/Cleaner</tt><tt><br>
    </tt><tt>      71: invokevirtual #11                 // Method
      sun/misc/Cleaner.clean:()V</tt><tt><br>
    </tt><tt>      74: goto          0</tt><tt><br>
    </tt><tt>      77: aload_1       </tt><tt><br>
    </tt><tt>      78: getfield      #12                 // Field
      java/lang/ref/Reference.queue:Ljava/lang/ref/ReferenceQueue;</tt><tt><br>
    </tt><tt>      81: astore_2      </tt><tt><br>
    </tt><tt>      82: aload_2       </tt><tt><br>
    </tt><tt>      83: getstatic     #13                 // Field
      java/lang/ref/ReferenceQueue.NULL:Ljava/lang/ref/ReferenceQueue;</tt><tt><br>
    </tt><tt>      86: if_acmpeq     95</tt><tt><br>
    </tt><tt>      89: aload_2       </tt><tt><br>
    </tt><tt>      90: aload_1       </tt><tt><br>
    </tt><tt>      91: invokevirtual #14                 // Method
      java/lang/ref/ReferenceQueue.enqueue:(Ljava/lang/ref/Reference;)Z</tt><tt><br>
    </tt><tt>      94: pop           </tt><tt><br>
    </tt><tt>      95: goto          0</tt><tt><br>
    </tt><tt>    Exception table:</tt><tt><br>
    </tt><tt>       from    to  target type</tt><tt><br>
    </tt><b><tt>          33    39    42   Class
        java/lang/InterruptedException</tt></b><b><tt><br>
      </tt></b><b><tt>          33    39    42   Class
        java/lang/OutOfMemoryError</tt></b><tt><br>
    </tt><tt>           6    45    53   any</tt><tt><br>
    </tt><tt>          48    50    53   any</tt><tt><br>
    </tt><tt>          53    57    53   any</tt><tt><br>
    </tt><br>
    catch (OutOfMemoryError | InterruptedException x) variant has the
    exactly same bytecodes but the following exception table:<br>
    <br>
    <tt>    Exception table:</tt><tt><br>
    </tt><tt>       from    to  target type</tt><tt><br>
    </tt><b><tt>          33    39    42   Class
        java/lang/OutOfMemoryError</tt></b><b><tt><br>
      </tt></b><b><tt>          33    39    42   Class
        java/lang/InterruptedException</tt></b><tt><br>
    </tt><tt>           6    45    53   any</tt><tt><br>
    </tt><tt>          48    50    53   any</tt><tt><br>
    </tt><tt>          53    57    53   any</tt><br>
    <br>
    <br>
    ... 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).<br>
    <br>
    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):<br>
    <br>
    <br>
    <tt>                        try {</tt><tt><br>
    </tt><tt>                            lock.wait();</tt><tt><br>
    </tt><tt>                        } catch (OutOfMemoryError x) { }</tt><tt><br>
    </tt><tt>                          catch (InterruptedException x) {
      }</tt><br>
    <br>
    <br>
    ...the benefit of this is that OOME is never thrown two times.<br>
    <br>
    Regards, Peter<br>
    <br>
  </body>
</html>