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(-)

diff --git a/src/hotspot/cpu/arm/vm_version_arm_32.cpp b/src/hotspot/cpu/arm/vm_version_arm_32.cpp
index d09419360..81b6c33fe 100644
--- a/src/hotspot/cpu/arm/vm_version_arm_32.cpp
+++ b/src/hotspot/cpu/arm/vm_version_arm_32.cpp
@@ -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()) {
diff --git a/src/hotspot/cpu/riscv/vm_version_riscv.cpp b/src/hotspot/cpu/riscv/vm_version_riscv.cpp
index fb04c2723..2fefdede9 100644
--- a/src/hotspot/cpu/riscv/vm_version_riscv.cpp
+++ b/src/hotspot/cpu/riscv/vm_version_riscv.cpp
@@ -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
diff --git a/src/hotspot/cpu/s390/vm_version_s390.cpp b/src/hotspot/cpu/s390/vm_version_s390.cpp
index 8261fbd08..605e6c91e 100644
--- a/src/hotspot/cpu/s390/vm_version_s390.cpp
+++ b/src/hotspot/cpu/s390/vm_version_s390.cpp
@@ -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
diff --git a/src/hotspot/cpu/x86/vm_version_x86.cpp b/src/hotspot/cpu/x86/vm_version_x86.cpp
index b8a7149d4..927729d6f 100644
--- a/src/hotspot/cpu/x86/vm_version_x86.cpp
+++ b/src/hotspot/cpu/x86/vm_version_x86.cpp
@@ -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())) {
diff --git a/src/hotspot/cpu/zero/vm_version_zero.cpp b/src/hotspot/cpu/zero/vm_version_zero.cpp
index 3ce9227c1..9f95f0517 100644
--- a/src/hotspot/cpu/zero/vm_version_zero.cpp
+++ b/src/hotspot/cpu/zero/vm_version_zero.cpp
@@ -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);
diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp
index 23759ed7e..fb15a352f 100644
--- a/src/hotspot/share/prims/unsafe.cpp
+++ b/src/hotspot/share/prims/unsafe.cpp
@@ -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)},
diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp
index 12ddfa5ab..c5749d6db 100644
--- a/src/hotspot/share/runtime/globals.hpp
+++ b/src/hotspot/share/runtime/globals.hpp
@@ -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.")                  \
                                                                             \
diff --git a/src/java.base/share/classes/java/util/HashMap.java b/src/java.base/share/classes/java/util/HashMap.java
index 3320b394e..857def51b 100644
--- a/src/java.base/share/classes/java/util/HashMap.java
+++ b/src/java.base/share/classes/java/util/HashMap.java
@@ -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
diff --git a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
index 35c1effdc..6684ed58b 100644
--- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
+++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java
@@ -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