From 48dd5d75770b50e82f14f4a4def611970241eb34 Mon Sep 17 00:00:00 2001
Date: Thu, 21 May 2026 16:29:27 +0800
Subject: [PATCH] Add HashMap FrontCache
src/hotspot/cpu/arm/vm_version_arm_32.cpp | 10 +
src/hotspot/cpu/riscv/vm_version_riscv.cpp | 10 +
src/hotspot/cpu/s390/vm_version_s390.cpp | 2 +-
src/hotspot/cpu/x86/vm_version_x86.cpp | 5 +
src/hotspot/cpu/zero/vm_version_zero.cpp | 10 +
src/hotspot/share/prims/unsafe.cpp | 7 +
src/hotspot/share/runtime/globals.hpp | 5 +
.../share/classes/java/util/HashMap.java | 192 +++++++++++++++++-
.../classes/jdk/internal/misc/Unsafe.java | 2 +-
9 files changed, 240 insertions(+), 3 deletions(-)
@@ -268,6 +268,16 @@ void VM_Version::initialize() {
FLAG_SET_DEFAULT(UseVectorizedMismatchIntrinsic, false);
}
+ if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) {
+ FLAG_SET_DEFAULT(UseHashMapIntegerCache, false);
+ warning("HashMap optimization is not supported in this VM.");
+ }
+
+ if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) {
+ FLAG_SET_DEFAULT(UseFastSerializer, false);
+ warning("Serializer optimization is not supported in this VM.");
+ }
+
#ifdef COMPILER2
// C2 is only supported on v7+ VFP at this time
if (_arm_arch < 7 || !has_vfp()) {
@@ -246,6 +246,16 @@ void VM_Version::common_initialize() {
FLAG_SET_DEFAULT(UseZvfh, false);
}
}
+
+ if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) {
+ FLAG_SET_DEFAULT(UseHashMapIntegerCache, false);
+ warning("HashMap optimization is not supported in this VM.");
+ }
+
+ if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) {
+ FLAG_SET_DEFAULT(UseFastSerializer, false);
+ warning("Serializer optimization is not supported in this VM.");
+ }
}
#ifdef COMPILER2
@@ -14,7 +14,7 @@
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
+ * 2 along with this work; if not, write to the Free Software Fundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
@@ -1336,6 +1336,11 @@ void VM_Version::get_processor_features() {
FLAG_SET_DEFAULT(UseFastSerializer, false);
warning("Serializer optimization is not supported in this VM.");
}
+ if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) {
+ FLAG_SET_DEFAULT(UseHashMapIntegerCache, false);
+ warning("HashMap optimization is not supported in this VM.");
+ }
+
#endif
if (UseSHA && supports_avx2() && (supports_bmi2() || supports_sha512())) {
@@ -70,6 +70,16 @@ void VM_Version::initialize() {
FLAG_SET_DEFAULT(UseFMA, false);
}
+ if (UnlockExperimentalVMOptions && UseHashMapIntegerCache && !FLAG_IS_DEFAULT(UseHashMapIntegerCache)) {
+ FLAG_SET_DEFAULT(UseHashMapIntegerCache, false);
+ warning("HashMap optimization is not supported in this VM.");
+ }
+
+ if (UnlockExperimentalVMOptions && UseFastSerializer && !FLAG_IS_DEFAULT(UseFastSerializer)) {
+ FLAG_SET_DEFAULT(UseFastSerializer, false);
+ warning("Serializer optimization is not supported in this VM.");
+ }
+
if (UseMD5Intrinsics) {
warning("MD5 intrinsics are not available on this CPU");
FLAG_SET_DEFAULT(UseMD5Intrinsics, false);
@@ -835,6 +835,11 @@ UNSAFE_ENTRY(jint, Unsafe_GetLoadAverage0(JNIEnv *env, jobject unsafe, jdoubleAr
return ret;
} UNSAFE_END
+UNSAFE_ENTRY(jboolean, Unsafe_GetUseHashMapIntegerCache(JNIEnv *env, jobject unsafe)) {
+ return UseHashMapIntegerCache;
+}
+UNSAFE_END
+
UNSAFE_ENTRY(jboolean, Unsafe_GetUseFastSerializer(JNIEnv *env, jobject unsafe)) {
return UseFastSerializer;
}
@@ -915,6 +920,8 @@ static JNINativeMethod jdk_internal_misc_Unsafe_methods[] = {
{CC "writebackPostSync0", CC "()V", FN_PTR(Unsafe_WriteBackPostSync0)},
{CC "setMemory0", CC "(" OBJ "JJB)V", FN_PTR(Unsafe_SetMemory0)},
+ {CC "getUseHashMapIntegerCache", CC "()Z", FN_PTR(Unsafe_GetUseHashMapIntegerCache)}
+ ,
{CC "shouldBeInitialized0", CC "(" CLS ")Z", FN_PTR(Unsafe_ShouldBeInitialized0)},
{CC "fullFence", CC "()V", FN_PTR(Unsafe_FullFence)},
@@ -1957,6 +1957,11 @@ const int ObjectAlignmentInBytes = 8;
product(bool, UseFastUnorderedTimeStamps, false, EXPERIMENTAL, \
"Use platform unstable time where supported for timestamps only") \
\
+ product(bool, UseHashMapIntegerCache, false, EXPERIMENTAL, \
+ "The integer cache is an array of references to objects of" \
+ "the HashMap Value type, indexed by the unboxed int key value." \
+ "faster in execution, higher in memory consumption.") \
+ \
product(bool, DeoptimizeNMethodBarriersALot, false, DIAGNOSTIC, \
"Make nmethod barriers deoptimise a lot.") \
\
@@ -36,6 +36,7 @@ import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import jdk.internal.access.SharedSecrets;
+import jdk.internal.misc.Unsafe;
/**
* Hash table based implementation of the {@code Map} interface. This
@@ -274,6 +275,28 @@ public class HashMap<K,V> extends AbstractMap<K,V>
*/
static final int MIN_TREEIFY_CAPACITY = 64;
+ /**
+ * Used to get the commandline option: UseHashMapIntegerCache.
+ */
+ private static final Unsafe UNSAFE = Unsafe.getUnsafe();
+
+ /**
+ * Indicate integerCache can be performed. disable if HashMap.Node.setValue
+ * is directly used to update Node value.
+ */
+ private static boolean enableIntegerCache = UNSAFE.getUseHashMapIntegerCache();
+
+ /**
+ * The smallest table size for create integer cache.
+ */
+ private static final int MIN_INTEGER_CACHE = 2048;
+
+ /**
+ * The factor used in create integer cache to guarantee most Key are
+ * Integer and in range.
+ */
+ private static final float INTEGER_CACHE_FACTOR = 0.95f;
+
/**
* Basic hash bin node, used for most entries. (See below for
* TreeNode subclass, and in LinkedHashMap for its Entry subclass.)
@@ -300,6 +323,10 @@ public class HashMap<K,V> extends AbstractMap<K,V>
}
public final V setValue(V newValue) {
+ // Disable integerCache in all HashMap instance.
+ if (key != null && key instanceof Integer) {
+ enableIntegerCache = false;
+ }
V oldValue = value;
value = newValue;
return oldValue;
@@ -389,6 +416,12 @@ public class HashMap<K,V> extends AbstractMap<K,V>
*/
transient Node<K,V>[] table;
+ /**
+ * Cache <Integer key -> Value> Map
+ * integerCache[key->intValue] = V
+ */
+ transient Object[] integerCache;
+
/**
* Holds cached entrySet(). Note that AbstractMap fields are used
* for keySet() and values().
@@ -560,7 +593,20 @@ public class HashMap<K,V> extends AbstractMap<K,V>
*
* @see #put(Object, Object)
*/
+ @SuppressWarnings("unchecked")
public V get(Object key) {
+ if (integerCache != null) {
+ if (enableIntegerCache == false) {
+ integerCache = null;
+ }
+ else if (key != null && key instanceof Integer) {
+ int val = ((Integer)key).intValue();
+ if (val >= 0 && val < integerCache.length) {
+ return (V)integerCache[val];
+ }
+ }
+ }
+
Node<K,V> e;
return (e = getNode(key)) == null ? null : e.value;
}
@@ -600,7 +646,18 @@ public class HashMap<K,V> extends AbstractMap<K,V>
* key.
*/
public boolean containsKey(Object key) {
- return getNode(key) != null;
+ if (integerCache != null) {
+ if (enableIntegerCache == false) {
+ integerCache = null;
+ }
+ else if (key != null && key instanceof Integer) {
+ int val = ((Integer)key).intValue();
+ if (val >= 0 && val < integerCache.length && integerCache[val] != null) {
+ return true;
+ }
+ }
+ }
+ return getNode(key) != null;
}
/**
@@ -632,6 +689,11 @@ public class HashMap<K,V> extends AbstractMap<K,V>
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
+
+ if (integerCache != null) {
+ updateIntegerCache(key, value, onlyIfAbsent);
+ }
+
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
@@ -752,6 +814,8 @@ public class HashMap<K,V> extends AbstractMap<K,V>
}
}
}
+
+ createIntegerCache();
return newTab;
}
@@ -851,6 +915,10 @@ public class HashMap<K,V> extends AbstractMap<K,V>
p.next = node.next;
++modCount;
--size;
+
+ if (integerCache != null) {
+ updateIntegerCache(node.key, null, false);
+ }
afterNodeRemoval(node);
return node;
}
@@ -870,6 +938,7 @@ public class HashMap<K,V> extends AbstractMap<K,V>
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
+ integerCache = null;
}
/**
@@ -894,6 +963,82 @@ public class HashMap<K,V> extends AbstractMap<K,V>
return false;
}
+ /**
+ * 1. iterator all Keys and statistic
+ * Integer Key count, total count is size
+ * Integer Key count in range [0, table.length], get Max value.
+ *
+ * 2. Create integer cache
+ */
+ @SuppressWarnings({"unchecked"})
+ private final void createIntegerCache() {
+ int n = table.length;
+ int intKeyCount = 0;
+ int intKeyCountInrange = 0;
+ int maxIntKey = 0;
+ if (n < MIN_INTEGER_CACHE || (enableIntegerCache == false)) {
+ integerCache = null;
+ return;
+ }
+ Iterator<K> it = this.keySet().iterator();
+ while (it.hasNext()) {
+ K key = it.next();
+ if (key != null && key instanceof Integer) {
+ intKeyCount++;
+ int val = ((Integer)key).intValue();
+ if (val >= 0 && val < n) {
+ intKeyCountInrange++;
+ if (val > maxIntKey)
+ maxIntKey = val;
+ }
+ }
+ }
+ float keyIntRation = ((float)intKeyCount) / size;
+ float keyIntInRangeRation = ((float)intKeyCountInrange) / size;
+ if (keyIntRation >= INTEGER_CACHE_FACTOR &&
+ keyIntInRangeRation >= INTEGER_CACHE_FACTOR) {
+ // compute integerCache size
+ int cacheMapSize = n < (2 * maxIntKey) ? n : (2 * maxIntKey);
+ integerCache = new Object[cacheMapSize];
+ Iterator<Map.Entry<K,V>> entries = this.entrySet().iterator();
+ while (entries.hasNext()) {
+ Map.Entry<K,V> thisEntry = entries.next();
+ K key = thisEntry.getKey();
+ V value = thisEntry.getValue();
+ if (key != null && key instanceof Integer) {
+ int val = ((Integer)key).intValue();
+ if (val >= 0 && val < integerCache.length) {
+ integerCache[val] = value;
+ }
+ }
+ }
+ } else {
+ integerCache = null;
+ }
+ }
+
+ /**
+ * put if integerCache null check outside of this call
+ * JIT will not inline this method (not hot) when HashMap is not Integer Key intensive.
+ * Otherwise it will always inline updateIntegerCache method.
+ *
+ */
+ private final void updateIntegerCache(K key, V value, boolean onlyIfAbsent) {
+ if (enableIntegerCache == false) {
+ integerCache = null;
+ return;
+ }
+ if (key != null && key instanceof Integer) {
+ int val = ((Integer)key).intValue();
+ if (val >= 0 && val < integerCache.length) {
+ if (onlyIfAbsent && integerCache[val] != null) {
+ return;
+ }
+ integerCache[val] = value;
+ }
+ }
+ }
+
/**
* Returns a {@link Set} view of the keys contained in this map.
* The set is backed by the map, so changes to the map are
@@ -1143,7 +1288,19 @@ public class HashMap<K,V> extends AbstractMap<K,V>
// Overrides of JDK8 Map extension methods
@Override
+ @SuppressWarnings("unchecked")
public V getOrDefault(Object key, V defaultValue) {
+ if (integerCache != null) {
+ if (enableIntegerCache == false) {
+ integerCache = null;
+ } else if (key != null && key instanceof Integer) {
+ V value;
+ int val = ((Integer)key).intValue();
+ if (val >= 0 && val < integerCache.length && (value = (V)integerCache[val]) != null) {
+ return value;
+ }
+ }
+ }
Node<K,V> e;
return (e = getNode(key)) == null ? defaultValue : e.value;
}
@@ -1164,6 +1321,9 @@ public class HashMap<K,V> extends AbstractMap<K,V>
if ((e = getNode(key)) != null &&
((v = e.value) == oldValue || (v != null && v.equals(oldValue)))) {
e.value = newValue;
+ if (integerCache != null) {
+ updateIntegerCache(key, newValue, false);
+ }
afterNodeAccess(e);
return true;
}
@@ -1176,6 +1336,9 @@ public class HashMap<K,V> extends AbstractMap<K,V>
if ((e = getNode(key)) != null) {
V oldValue = e.value;
e.value = value;
+ if (integerCache != null) {
+ updateIntegerCache(key, value, false);
+ }
afterNodeAccess(e);
return oldValue;
}
@@ -1232,6 +1395,9 @@ public class HashMap<K,V> extends AbstractMap<K,V>
return null;
} else if (old != null) {
old.value = v;
+ if (integerCache != null) {
+ updateIntegerCache(key, v, false);
+ }
afterNodeAccess(old);
return v;
}
@@ -1242,6 +1408,11 @@ public class HashMap<K,V> extends AbstractMap<K,V>
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
+
+ if (integerCache != null) {
+ updateIntegerCache(key, v, false);
+ }
+
modCount = mc + 1;
++size;
afterNodeInsertion(true);
@@ -1271,6 +1442,9 @@ public class HashMap<K,V> extends AbstractMap<K,V>
if (mc != modCount) { throw new ConcurrentModificationException(); }
if (v != null) {
e.value = v;
+ if (integerCache != null) {
+ updateIntegerCache(key, v, false);
+ }
afterNodeAccess(e);
return v;
}
@@ -1327,6 +1501,9 @@ public class HashMap<K,V> extends AbstractMap<K,V>
if (old != null) {
if (v != null) {
old.value = v;
+ if (integerCache != null) {
+ updateIntegerCache(key, v, false);
+ }
afterNodeAccess(old);
}
else
@@ -1340,6 +1517,9 @@ public class HashMap<K,V> extends AbstractMap<K,V>
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
+ if (integerCache != null) {
+ updateIntegerCache(key, v, false);
+ }
modCount = mc + 1;
++size;
afterNodeInsertion(true);
@@ -1398,6 +1578,9 @@ public class HashMap<K,V> extends AbstractMap<K,V>
}
if (v != null) {
old.value = v;
+ if (integerCache != null) {
+ updateIntegerCache(key, v, false);
+ }
afterNodeAccess(old);
}
else
@@ -1411,6 +1594,9 @@ public class HashMap<K,V> extends AbstractMap<K,V>
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
}
+ if (integerCache != null) {
+ updateIntegerCache(key, value, false);
+ }
++modCount;
++size;
afterNodeInsertion(true);
@@ -1444,6 +1630,9 @@ public class HashMap<K,V> extends AbstractMap<K,V>
for (Node<K,V> e : tab) {
for (; e != null; e = e.next) {
e.value = function.apply(e.key, e.value);
+ if (integerCache != null) {
+ updateIntegerCache(e.key, e.value, false);
+ }
}
}
if (modCount != mc)
@@ -1936,6 +2125,7 @@ public class HashMap<K,V> extends AbstractMap<K,V>
modCount = 0;
threshold = 0;
size = 0;
+ integerCache = null;
}
// Callbacks to allow LinkedHashMap post-actions
@@ -3834,7 +3834,7 @@ public final class Unsafe {
private static int convEndian(boolean big, int n) { return big == BIG_ENDIAN ? n : Integer.reverseBytes(n) ; }
private static long convEndian(boolean big, long n) { return big == BIG_ENDIAN ? n : Long.reverseBytes(n) ; }
-
+ public native boolean getUseHashMapIntegerCache();
public native boolean getUseFastSerializer();
private native long allocateMemory0(long bytes);
private native long reallocateMemory0(long address, long bytes);
--
2.33.0