< prev index next >

src/java.base/share/classes/java/io/ObjectInputStream.java

Print this page

        

@@ -417,10 +417,21 @@
             return readObjectOverride();
         }
 
         // if nested read, passHandle contains handle of enclosing object
         int outerHandle = passHandle;
+        // save current cachedLoader
+        Object outerCL = cachedLoader;
+
+        Thread thread  = Thread.currentThread();
+        if (outerCL == null ||
+            outerCL == thread ||
+            (outerCL instanceof CachedLoader &&
+             ((CachedLoader) outerCL).thread == thread)) {
+            // place mark so that latestUserDefinedLoader() can cache loader
+            cachedLoader = thread;
+        }
         try {
             Object obj = readObject0(false);
             handles.markDependency(outerHandle, passHandle);
             ClassNotFoundException ex = handles.lookupException(passHandle);
             if (ex != null) {

@@ -434,10 +445,17 @@
         } finally {
             passHandle = outerHandle;
             if (closed && depth == 0) {
                 clear();
             }
+            if (outerCL == null ||
+                outerCL == thread ||
+                (outerCL instanceof CachedLoader &&
+                 ((CachedLoader) outerCL).thread == thread)) {
+                // restore/clear cachedLoader when in correct thread/non-nested call
+                cachedLoader = outerCL;
+            }
         }
     }
 
     /**
      * This method is called by trusted subclasses of ObjectOutputStream that

@@ -512,10 +530,21 @@
      * @since   1.4
      */
     public Object readUnshared() throws IOException, ClassNotFoundException {
         // if nested read, passHandle contains handle of enclosing object
         int outerHandle = passHandle;
+        // save current cachedLoader
+        Object outerCL = cachedLoader;
+
+        Thread thread  = Thread.currentThread();
+        if (outerCL == null ||
+             outerCL == thread ||
+             (outerCL instanceof CachedLoader &&
+              ((CachedLoader) outerCL).thread == thread)) {
+            // place mark so that latestUserDefinedLoader() can cache loader
+            cachedLoader = thread;
+        }
         try {
             Object obj = readObject0(true);
             handles.markDependency(outerHandle, passHandle);
             ClassNotFoundException ex = handles.lookupException(passHandle);
             if (ex != null) {

@@ -529,10 +558,17 @@
         } finally {
             passHandle = outerHandle;
             if (closed && depth == 0) {
                 clear();
             }
+            if (outerCL == null ||
+                outerCL == thread ||
+                (outerCL instanceof CachedLoader &&
+                 ((CachedLoader) outerCL).thread == thread)) {
+                // restore/clear cachedLoader when in correct thread/non-nested call
+                cachedLoader = outerCL;
+            }
         }
     }
 
     /**
      * Read the non-static and non-transient fields of the current class from

@@ -2387,18 +2423,54 @@
     // REMIND: remove once hotspot inlines Double.longBitsToDouble
     private static native void bytesToDoubles(byte[] src, int srcpos,
                                               double[] dst, int dstpos,
                                               int ndoubles);
 
+    // cached latestUserDefinedLoader() result
+    private static class CachedLoader {
+        final ClassLoader loader;
+        final Thread thread = Thread.currentThread();
+
+        CachedLoader(ClassLoader loader) {
+            this.loader = loader;
+        }
+    }
+
+    // either null (when not called via public readObject() / readUnshared()), or
+    // a Thread instance (marking the thread that entered public readObject() /
+    // readUnshared() and is responsible for cleanup too), or
+    // a CachedLoader instance with cached loader
+    private Object cachedLoader;
+
     /**
      * Returns the first non-null and non-platform class loader (not counting
      * class loaders of generated reflection implementation classes) up the
      * execution stack, or the platform class loader if only code from the
      * bootstrap and platform class loader is on the stack.
      */
-    private static ClassLoader latestUserDefinedLoader() {
-        return jdk.internal.misc.VM.latestUserDefinedLoader();
+    private ClassLoader latestUserDefinedLoader() {
+        Object cl = cachedLoader;
+        ClassLoader loader;
+        Thread thread = Thread.currentThread();
+        if (cl == thread) {
+            // entered via public readObject() / readUnshared()
+            // so we must evaluate loader lazily and cache it
+            loader = jdk.internal.misc.VM.latestUserDefinedLoader();
+            cachedLoader = new CachedLoader(loader);
+        } else if (cl instanceof CachedLoader &&
+                   ((CachedLoader) cl).thread == thread) {
+            // use cached value if correct thread
+            loader = ((CachedLoader) cl).loader;
+        } else {
+            // not called via public readObject() / readUnshared():
+            //   (cl == null) or
+            // invalid multi threaded use:
+            //   (cl != Thread.currentThread() && cl.thread != Thread.currentThread())
+            // - don't cache
+            loader = jdk.internal.misc.VM.latestUserDefinedLoader();
+        }
+        return loader;
     }
 
     /**
      * Default GetField implementation.
      */
< prev index next >