< prev index next >
src/java.base/share/classes/java/io/ObjectInputStream.java
Print this page
@@ -417,10 +417,23 @@
return readObjectOverride();
}
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
+ // save currentReader
+ Object outerReader = currentReader;
+
+ Thread thread = Thread.currentThread();
+ if (outerReader == null ||
+ outerReader == thread ||
+ (outerReader instanceof ReaderContext &&
+ ((ReaderContext) outerReader).thread == thread)) {
+ // If no other thead caches ReaderContext or this is recursive entry,
+ // place mark so that latestUserDefinedLoader() can cache loader in
+ // this context
+ currentReader = thread;
+ }
try {
Object obj = readObject0(false);
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
@@ -434,10 +447,17 @@
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
+ if (outerReader == null ||
+ outerReader == thread ||
+ (outerReader instanceof ReaderContext &&
+ ((ReaderContext) outerReader).thread == thread)) {
+ // restore/clear currentReader when in correct thread/non-nested call
+ currentReader = outerReader;
+ }
}
}
/**
* This method is called by trusted subclasses of ObjectOutputStream that
@@ -512,10 +532,23 @@
* @since 1.4
*/
public Object readUnshared() throws IOException, ClassNotFoundException {
// if nested read, passHandle contains handle of enclosing object
int outerHandle = passHandle;
+ // save currentReader
+ Object outerReader = currentReader;
+
+ Thread thread = Thread.currentThread();
+ if (outerReader == null ||
+ outerReader == thread ||
+ (outerReader instanceof ReaderContext &&
+ ((ReaderContext) outerReader).thread == thread)) {
+ // If no other thead caches ReaderContext or this is recursive entry,
+ // place mark so that latestUserDefinedLoader() can cache loader in
+ // this context
+ currentReader = thread;
+ }
try {
Object obj = readObject0(true);
handles.markDependency(outerHandle, passHandle);
ClassNotFoundException ex = handles.lookupException(passHandle);
if (ex != null) {
@@ -529,10 +562,17 @@
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
clear();
}
+ if (outerReader == null ||
+ outerReader == thread ||
+ (outerReader instanceof ReaderContext &&
+ ((ReaderContext) outerReader).thread == thread)) {
+ // restore/clear currentReader when in correct thread/non-nested call
+ currentReader = outerReader;
+ }
}
}
/**
* Read the non-static and non-transient fields of the current class from
@@ -2387,18 +2427,64 @@
// REMIND: remove once hotspot inlines Double.longBitsToDouble
private static native void bytesToDoubles(byte[] src, int srcpos,
double[] dst, int dstpos,
int ndoubles);
+ // Holds a pair of latestUserDefinedLoader() result and the thread that called
+ // the method, so that latestUserDefinedLoader() can identify if it can reuse
+ // the cached result.
+ private static class ReaderContext {
+ final ClassLoader loader;
+ final Thread thread = Thread.currentThread();
+
+ ReaderContext(ClassLoader loader) {
+ this.loader = loader;
+ }
+ }
+
+ // Records the thread that can cache latestUserDefinedLoader. This field containes
+ // 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 ReaderContext instance with cached loader.
+ // On recursive entry to public readObject() / readUnshared(), the current value
+ // of this field is saved in a local variable and reset to the Thread instance,
+ // so that the nexted latestUserDefinedLoader() can evaluate loader in the context.
+ // This field is left non-volatile because this field does not intend to limit
+ // only one thread to use ReaderContext but to make sure a thread uses correct
+ // ReaderContext and cleans it up on return from readObject() / readUnshared().
+ private Object currentReader;
+
/**
* 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 readerCtx = currentReader;
+ ClassLoader loader;
+ Thread thread = Thread.currentThread();
+ if (readerCtx == thread) {
+ // entered via public readObject() / readUnshared()
+ // so we must evaluate loader lazily and cache it
+ loader = jdk.internal.misc.VM.latestUserDefinedLoader();
+ currentReader = new ReaderContext(loader);
+ } else if (readerCtx instanceof ReaderContext &&
+ ((ReaderContext) readerCtx).thread == thread) {
+ // use cached value if correct thread
+ loader = ((ReaderContext) readerCtx).loader;
+ } else {
+ // not called via public readObject() / readUnshared():
+ // (readerCtx == null) or
+ // invalid multi threaded use:
+ // (readerCtx != Thread.currentThread() &&
+ // readerCtx.thread != Thread.currentThread())
+ // - don't cache
+ loader = jdk.internal.misc.VM.latestUserDefinedLoader();
+ }
+ return loader;
}
/**
* Default GetField implementation.
*/
< prev index next >