6806649: synchronization bottleneck when constructing Thread subclasses
Summary: Replace subclass audits synchronization with ConcurrentHashMap with weakly referenced Class keys
Reviewed-by: peterjones, dholmes, martin
--- a/jdk/src/share/classes/java/lang/Thread.java Mon Feb 23 17:32:52 2009 +0800
+++ b/jdk/src/share/classes/java/lang/Thread.java Mon Feb 23 10:36:19 2009 +0000
@@ -25,13 +25,17 @@
package java.lang;
+import java.lang.ref.Reference;
+import java.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
import java.security.AccessController;
import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.Map;
import java.util.HashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.locks.LockSupport;
-import sun.misc.SoftCache;
import sun.nio.ch.Interruptible;
import sun.security.util.SecurityConstants;
@@ -1640,8 +1644,17 @@
new RuntimePermission("enableContextClassLoaderOverride");
/** cache of subclass security audit results */
- private static final SoftCache subclassAudits = new SoftCache(10);
+ /* Replace with ConcurrentReferenceHashMap when/if it appears in a future
+ * release */
+ private static class Caches {
+ /** cache of subclass security audit results */
+ static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
+ new ConcurrentHashMap<WeakClassKey,Boolean>();
+ /** queue for WeakReferences to audited subclasses */
+ static final ReferenceQueue<Class<?>> subclassAuditsQueue =
+ new ReferenceQueue<Class<?>>();
+ }
/**
* Verifies that this (possibly subclass) instance can be constructed
@@ -1652,19 +1665,15 @@
private static boolean isCCLOverridden(Class cl) {
if (cl == Thread.class)
return false;
- Boolean result = null;
- synchronized (subclassAudits) {
- result = (Boolean) subclassAudits.get(cl);
- if (result == null) {
- /*
- * Note: only new Boolean instances (i.e., not Boolean.TRUE or
- * Boolean.FALSE) must be used as cache values, otherwise cache
- * entry will pin associated class.
- */
- result = new Boolean(auditSubclass(cl));
- subclassAudits.put(cl, result);
- }
+
+ processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
+ WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
+ Boolean result = Caches.subclassAudits.get(key);
+ if (result == null) {
+ result = Boolean.valueOf(auditSubclass(cl));
+ Caches.subclassAudits.putIfAbsent(key, result);
}
+
return result.booleanValue();
}
@@ -1967,6 +1976,68 @@
getUncaughtExceptionHandler().uncaughtException(this, e);
}
+ /**
+ * Removes from the specified map any keys that have been enqueued
+ * on the specified reference queue.
+ */
+ static void processQueue(ReferenceQueue<Class<?>> queue,
+ ConcurrentMap<? extends
+ WeakReference<Class<?>>, ?> map)
+ {
+ Reference<? extends Class<?>> ref;
+ while((ref = queue.poll()) != null) {
+ map.remove(ref);
+ }
+ }
+
+ /**
+ * Weak key for Class objects.
+ **/
+ static class WeakClassKey extends WeakReference<Class<?>> {
+ /**
+ * saved value of the referent's identity hash code, to maintain
+ * a consistent hash code after the referent has been cleared
+ */
+ private final int hash;
+
+ /**
+ * Create a new WeakClassKey to the given object, registered
+ * with a queue.
+ */
+ WeakClassKey(Class<?> cl, ReferenceQueue<Class<?>> refQueue) {
+ super(cl, refQueue);
+ hash = System.identityHashCode(cl);
+ }
+
+ /**
+ * Returns the identity hash code of the original referent.
+ */
+ @Override
+ public int hashCode() {
+ return hash;
+ }
+
+ /**
+ * Returns true if the given object is this identical
+ * WeakClassKey instance, or, if this object's referent has not
+ * been cleared, if the given object is another WeakClassKey
+ * instance with the identical non-null referent as this one.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+
+ if (obj instanceof WeakClassKey) {
+ Object referent = get();
+ return (referent != null) &&
+ (referent == ((WeakClassKey) obj).get());
+ } else {
+ return false;
+ }
+ }
+ }
+
/* Some private helper methods */
private native void setPriority0(int newPriority);
private native void stop0(Object o);