src/share/classes/java/util/Hashtable.java
Print this page
rev 5810 : 8006593: Performance and compatibility improvements to hash based Map implementations.
Summary: Use ThreadLocalRandom for hash seed rather than shared Random. Initialize {HashMap|Hashtable}.hashSeed only as needed. Minor optimizations.
Reviewed-by: alanb, bchristi
@@ -214,55 +214,38 @@
/**
* If {@code true} then perform alternative hashing of String keys to reduce
* the incidence of collisions due to weak hash code calculation.
*/
- transient boolean useAltHashing;
+ transient boolean useAltHashing = false;
- // Unsafe mechanics
/**
- * Unsafe utilities
+ * A randomizing value associated with this instance that is applied to
+ * hash code of keys to make hash collisions harder to find.
*/
- private static final sun.misc.Unsafe UNSAFE;
+ transient int hashSeed;
/**
- * Offset of "final" hashSeed field we must set in readObject() method.
+ * Initialize the hashing mask value.
*/
- private static final long HASHSEED_OFFSET;
-
- static {
- try {
- UNSAFE = sun.misc.Unsafe.getUnsafe();
- HASHSEED_OFFSET = UNSAFE.objectFieldOffset(
- Hashtable.class.getDeclaredField("hashSeed"));
- } catch (NoSuchFieldException | SecurityException e) {
- throw new Error("Failed to record hashSeed offset", e);
+ final boolean initHashSeedAsNeeded(int capacity) {
+ boolean currentAltHashing = useAltHashing;
+ useAltHashing = sun.misc.VM.isBooted() &&
+ (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
+ boolean switching = currentAltHashing ^ useAltHashing;
+ if (switching) {
+ hashSeed = useAltHashing
+ ? sun.misc.Hashing.randomHashSeed(this)
+ : 0;
}
+ return switching;
}
- /**
- * A randomizing value associated with this instance that is applied to
- * hash code of keys to make hash collisions harder to find.
- */
- transient final int hashSeed = sun.misc.Hashing.randomHashSeed(this);
-
private int hash(Object k) {
- if (useAltHashing) {
- if (k.getClass() == String.class) {
- return sun.misc.Hashing.stringHash32((String) k);
- } else {
- int h = hashSeed ^ k.hashCode();
-
- // This function ensures that hashCodes that differ only by
- // constant multiples at each bit position have a bounded
- // number of collisions (approximately 8 at default load factor).
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
- }
- } else {
- return k.hashCode();
- }
+ // We don't test for useAltHashing because hashSeed will be zero if
+ // alternative hashing is disabled.
+ return hashSeed ^ k.hashCode();
}
/**
* Constructs a new, empty hashtable with the specified initial
* capacity and the specified load factor.
@@ -282,12 +265,11 @@
if (initialCapacity==0)
initialCapacity = 1;
this.loadFactor = loadFactor;
table = new Entry[initialCapacity];
threshold = (int)Math.min(initialCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
- useAltHashing = sun.misc.VM.isBooted() &&
- (initialCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
+ initHashSeedAsNeeded(initialCapacity);
}
/**
* Constructs a new, empty hashtable with the specified initial capacity
* and default load factor (0.75).
@@ -495,14 +477,11 @@
}
Entry<K,V>[] newMap = new Entry[newCapacity];
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);
- boolean currentAltHashing = useAltHashing;
- useAltHashing = sun.misc.VM.isBooted() &&
- (newCapacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
- boolean rehash = currentAltHashing ^ useAltHashing;
+ boolean rehash = initHashSeedAsNeeded(newCapacity);
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = oldMap[i] ; old != null ; ) {
@@ -997,14 +976,10 @@
throws IOException, ClassNotFoundException
{
// Read in the length, threshold, and loadfactor
s.defaultReadObject();
- // set hashSeed
- UNSAFE.putIntVolatile(this, HASHSEED_OFFSET,
- sun.misc.Hashing.randomHashSeed(this));
-
// Read the original length of the array and number of elements
int origlength = s.readInt();
int elements = s.readInt();
// Compute new size with a bit of room 5% to grow but
@@ -1015,24 +990,23 @@
if (length > elements && (length & 1) == 0)
length--;
if (origlength > 0 && length > origlength)
length = origlength;
- Entry<K,V>[] table = new Entry[length];
+ Entry<K,V>[] newTable = new Entry[length];
threshold = (int) Math.min(length * loadFactor, MAX_ARRAY_SIZE + 1);
count = 0;
- useAltHashing = sun.misc.VM.isBooted() &&
- (length >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
+ initHashSeedAsNeeded(length);
// Read the number of elements and then all the key/value objects
for (; elements > 0; elements--) {
K key = (K)s.readObject();
V value = (V)s.readObject();
// synch could be eliminated for performance
- reconstitutionPut(table, key, value);
+ reconstitutionPut(newTable, key, value);
}
- this.table = table;
+ this.table = newTable;
}
/**
* The put method used by readObject. This is provided because put
* is overridable and should not be called in readObject since the