OpenJDK / type-annotations / type-annotations / jdk
changeset 9406:d04102f69d46
8022321: java/lang/ref/OOMEInReferenceHandler.java fails intermittently
Summary: preload/preinitialize InterruptedException and Cleaner classes and catch OOME from instanceof operator in ReferenceHandler
Reviewed-by: dholmes, mchung, srikchan
author | plevart |
---|---|
date | Thu, 30 Jan 2014 15:36:04 +0100 |
parents | 97dc93591ae7 |
children | d9e4ca55da12 |
files | src/share/classes/java/lang/ref/Reference.java |
diffstat | 1 files changed, 47 insertions(+), 28 deletions(-) [+] |
line wrap: on
line diff
--- a/src/share/classes/java/lang/ref/Reference.java Wed Jan 29 22:18:46 2014 -0800 +++ b/src/share/classes/java/lang/ref/Reference.java Thu Jan 30 15:36:04 2014 +0100 @@ -111,7 +111,7 @@ * therefore critical that any code holding this lock complete as quickly * as possible, allocate no new objects, and avoid calling user code. */ - static private class Lock { }; + static private class Lock { } private static Lock lock = new Lock(); @@ -126,6 +126,22 @@ */ private static class ReferenceHandler extends Thread { + private static void ensureClassInitialized(Class<?> clazz) { + try { + Class.forName(clazz.getName(), true, clazz.getClassLoader()); + } catch (ClassNotFoundException e) { + throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e); + } + } + + static { + // pre-load and initialize InterruptedException and Cleaner classes + // so that we don't get into trouble later in the run loop if there's + // memory shortage while loading/initializing them lazily. + ensureClassInitialized(InterruptedException.class); + ensureClassInitialized(Cleaner.class); + } + ReferenceHandler(ThreadGroup g, String name) { super(g, name); } @@ -133,37 +149,40 @@ public void run() { for (;;) { Reference<Object> r; - synchronized (lock) { - if (pending != null) { - r = pending; - pending = r.discovered; - r.discovered = null; - } else { - // The waiting on the lock may cause an OOME because it may try to allocate - // exception objects, so also catch OOME here to avoid silent exit of the - // reference handler thread. - // - // Explicitly define the order of the two exceptions we catch here - // when waiting for the lock. - // - // We do not want to try to potentially load the InterruptedException class - // (which would be done if this was its first use, and InterruptedException - // were checked first) in this situation. - // - // This may lead to the VM not ever trying to load the InterruptedException - // class again. - try { - try { - lock.wait(); - } catch (OutOfMemoryError x) { } - } catch (InterruptedException x) { } - continue; + Cleaner c; + try { + synchronized (lock) { + if (pending != null) { + r = pending; + // 'instanceof' might throw OutOfMemoryError sometimes + // so do this before un-linking 'r' from the 'pending' chain... + c = r instanceof Cleaner ? (Cleaner) r : null; + // unlink 'r' from 'pending' chain + pending = r.discovered; + r.discovered = null; + } else { + // The waiting on the lock may cause an OutOfMemoryError + // because it may try to allocate exception objects. + lock.wait(); + continue; + } } + } catch (OutOfMemoryError x) { + // Give other threads CPU time so they hopefully drop some live references + // and GC reclaims some space. + // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above + // persistently throws OOME for some time... + Thread.yield(); + // retry + continue; + } catch (InterruptedException x) { + // retry + continue; } // Fast path for cleaners - if (r instanceof Cleaner) { - ((Cleaner)r).clean(); + if (c != null) { + c.clean(); continue; }