8022321: java/lang/ref/OOMEInReferenceHandler.java fails intermittently
authorplevart
Thu, 30 Jan 2014 15:36:04 +0100
changeset 22615 e929c13f8d11
parent 22614 2e2f77dc2599
child 22617 dcd0c84f4b06
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
jdk/src/share/classes/java/lang/ref/Reference.java
--- a/jdk/src/share/classes/java/lang/ref/Reference.java	Wed Jan 29 22:18:46 2014 -0800
+++ b/jdk/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;
                 }