On 11/05/2013 6:53 AM, Dean Long wrote:
On 5/10/2013 1:22 PM, Peter Levart wrote:

On 05/10/2013 10:08 PM, Peter Levart wrote:

On 05/10/2013 08:10 PM, Dean Long wrote:
If you really want to bullet-proof ReferenceHandler (and other
system threads) against OOME caused by native allocations,
then don't forget about monitor inflation if there is contention for
"lock" :-)

Inflation has already occurred if you do an Object.wait() as in this case.

Aren't monitors C++ objects? Are they allocated from java heap?

Ah, I see you're suggesting that "native" allocations can also trigger
OOME in Java program. Is this true?


That's what I was suggesting, but in the case of object monitors, it
looks like the VM will exit if it runs out.
Other places where we could fail to allocate native memory, such as
class loading or adding to the system dictionary,
might not exit the VM, but instead result in some other exception such
as NoSuchMethod or ClassNotFound.
I haven't investigated all the possibilities, but I just wanted to bring
it up as something to consider.

Right - most C-heap allocation failures in the VM result in an abort but some will convert to OOME (such as native thread creation related failures).

Any test that tries to force an OOME at a particular operation is going to be fragile because you can't know what other allocations might be needed under the covers. As Peter already discovered it might occur during the "new" of InterruptedException, or it might happen during the load/initialization of InterruptedException. So pre-initializing InterruptedException is probably a wise thing to do.

Aside we should also run these kind of heap-exhaustion tests with as small a heap as possible :)

David
-----

dl

Regards, Peter



dl

On 5/10/2013 6:14 AM, David Holmes wrote:
Hi Peter,

So it would appear that it is not in fact the "new" that causes the
OOME but the classloading of InterruptedException ?

I'm not sure I can quite get my head around this late on a Friday
night :)

David

On 10/05/2013 9: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: astore_2
        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: astore_1
       16: aload_1
       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: aload_1
       25: aconst_null
       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: astore_3
       43: aload_2
       44: monitorexit
       45: goto          0
       48: aload_2
       49: monitorexit
       50: goto          60
       53: astore        4
       55: aload_2
       56: monitorexit
       57: aload         4
       59: athrow
       60: aload_1
       61: instanceof    #10                 // class
sun/misc/Cleaner
       64: ifeq          77
       67: aload_1
       68: checkcast     #10                 // class
sun/misc/Cleaner
       71: invokevirtual #11                 // Method
sun/misc/Cleaner.clean:()V
       74: goto          0
       77: aload_1
       78: getfield      #12                 // Field
java/lang/ref/Reference.queue:Ljava/lang/ref/ReferenceQueue;
       81: astore_2
       82: aload_2
       83: getstatic     #13                 // Field
java/lang/ref/ReferenceQueue.NULL:Ljava/lang/ref/ReferenceQueue;
       86: if_acmpeq     95
       89: aload_2
       90: aload_1
       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





Reply via email to