--- old/src/share/classes/java/util/concurrent/ConcurrentHashMap.java 2012-05-22 21:33:17.000000000 -0700 +++ new/src/share/classes/java/util/concurrent/ConcurrentHashMap.java 2012-05-22 21:33:17.000000000 -0700 @@ -178,6 +178,55 @@ /* ---------------- Fields -------------- */ /** + * holds values which can't be initialized until after VM is booted. + */ + private static class Holder { + + /** + * Enable alternate hashing? + */ + static final boolean ALTERNATE_HASHING; + + static { + String altThreshold = java.security.AccessController.doPrivileged( + new sun.security.action.GetPropertyAction( + "jdk.map.althashing.threshold")); + + int threshold; + try { + threshold = (null != altThreshold) + ? Integer.parseInt(altThreshold) + : 1; + + if(threshold == -1) { + threshold = Integer.MAX_VALUE; + } + + if(threshold < 0) { + throw new IllegalArgumentException("value must be positive integer."); + } + } catch(IllegalArgumentException failed) { + throw new Error("Illegal value for 'jdk.map.althashing.threshold'", failed); + } + ALTERNATE_HASHING = threshold <= MAXIMUM_CAPACITY; + } + } + + /** + * A random mask value that is used for hashcode values associated with this + * instance to make hash collisions harder to find. + */ + private transient final int hashMask = makeHashMask(this); + + private static int makeHashMask(ConcurrentHashMap instance) { + if (sun.misc.VM.isBooted() && Holder.ALTERNATE_HASHING) { + return sun.misc.Hashing.makeHashMask(instance); + } + + return 0; + } + + /** * Mask value for indexing into segments. The upper bits of a * key's hash code are used to choose the segment. */ @@ -265,7 +314,15 @@ * that otherwise encounter collisions for hashCodes that do not * differ in lower or upper bits. */ - private static int hash(int h) { + private int hash(Object k) { + int h = hashMask; + + if ((0 != h) && (k instanceof String)) { + return sun.misc.Hashing.stringHash32((String) k) ^ h; + } + + h ^= k.hashCode(); + // Spread bits to regularize both segment and index locations, // using variant of single-word Wang/Jenkins hash. h += (h << 15) ^ 0xffffcd7d; @@ -919,7 +976,7 @@ public V get(Object key) { Segment s; // manually integrate access methods to reduce overhead HashEntry[] tab; - int h = hash(key.hashCode()); + int h = hash(key); long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; if ((s = (Segment)UNSAFE.getObjectVolatile(segments, u)) != null && (tab = s.table) != null) { @@ -947,7 +1004,7 @@ public boolean containsKey(Object key) { Segment s; // same as get() except no need for volatile value read HashEntry[] tab; - int h = hash(key.hashCode()); + int h = hash(key); long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; if ((s = (Segment)UNSAFE.getObjectVolatile(segments, u)) != null && (tab = s.table) != null) { @@ -1056,7 +1113,7 @@ Segment s; if (value == null) throw new NullPointerException(); - int hash = hash(key.hashCode()); + int hash = hash(key); int j = (hash >>> segmentShift) & segmentMask; if ((s = (Segment)UNSAFE.getObject // nonvolatile; recheck (segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment @@ -1076,7 +1133,7 @@ Segment s; if (value == null) throw new NullPointerException(); - int hash = hash(key.hashCode()); + int hash = hash(key); int j = (hash >>> segmentShift) & segmentMask; if ((s = (Segment)UNSAFE.getObject (segments, (j << SSHIFT) + SBASE)) == null) @@ -1106,7 +1163,7 @@ * @throws NullPointerException if the specified key is null */ public V remove(Object key) { - int hash = hash(key.hashCode()); + int hash = hash(key); Segment s = segmentForHash(hash); return s == null ? null : s.remove(key, hash, null); } @@ -1117,7 +1174,7 @@ * @throws NullPointerException if the specified key is null */ public boolean remove(Object key, Object value) { - int hash = hash(key.hashCode()); + int hash = hash(key); Segment s; return value != null && (s = segmentForHash(hash)) != null && s.remove(key, hash, value) != null; @@ -1129,7 +1186,7 @@ * @throws NullPointerException if any of the arguments are null */ public boolean replace(K key, V oldValue, V newValue) { - int hash = hash(key.hashCode()); + int hash = hash(key); if (oldValue == null || newValue == null) throw new NullPointerException(); Segment s = segmentForHash(hash); @@ -1144,7 +1201,7 @@ * @throws NullPointerException if the specified key or value is null */ public V replace(K key, V value) { - int hash = hash(key.hashCode()); + int hash = hash(key); if (value == null) throw new NullPointerException(); Segment s = segmentForHash(hash); @@ -1471,6 +1528,9 @@ private void readObject(java.io.ObjectInputStream s) throws IOException, ClassNotFoundException { s.defaultReadObject(); + + // set hashMask + UNSAFE.putIntVolatile(this, HASHMASK_OFFSET, makeHashMask(this)); // Re-initialize segments to be minimally sized, and let grow. int cap = MIN_SEGMENT_TABLE_CAPACITY; @@ -1499,6 +1559,7 @@ private static final int SSHIFT; private static final long TBASE; private static final int TSHIFT; + private static final long HASHMASK_OFFSET; static { int ss, ts; @@ -1510,6 +1571,8 @@ SBASE = UNSAFE.arrayBaseOffset(sc); ts = UNSAFE.arrayIndexScale(tc); ss = UNSAFE.arrayIndexScale(sc); + HASHMASK_OFFSET = UNSAFE.objectFieldOffset( + ConcurrentHashMap.class.getDeclaredField("hashMask")); } catch (Exception e) { throw new Error(e); }