<html>
  <head>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
  </head>
  <body bgcolor="#FFFFFF" text="#000000">
    <div class="moz-cite-prefix">Hi again,<br>
      <br>
      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.<br>
      <br>
      Regards, Peter<br>
      <br>
      On 05/10/2013 01:21 PM, Peter Levart wrote:<br>
    </div>
    <blockquote cite="mid:518CD83F.30906@gmail.com" type="cite">
      <meta content="text/html; charset=UTF-8" http-equiv="Content-Type">
      <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>
    </blockquote>
    <br>
  </body>
</html>