diff --git a/add-integerCache-feature.patch b/add-integerCache-feature.patch new file mode 100755 index 0000000000000000000000000000000000000000..0665764676cbfd8605656c3966fbb6c4f4985989 --- /dev/null +++ b/add-integerCache-feature.patch @@ -0,0 +1,413 @@ +diff --git a/src/hotspot/share/prims/unsafe.cpp b/src/hotspot/share/prims/unsafe.cpp +index d8f1679b4..18ea89b85 100644 +--- a/src/hotspot/share/prims/unsafe.cpp ++++ b/src/hotspot/share/prims/unsafe.cpp +@@ -1018,7 +1018,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; + } +@@ -1108,6 +1113,7 @@ static JNINativeMethod jdk_internal_misc_Unsafe_methods[] = { + {CC "fullFence", CC "()V", FN_PTR(Unsafe_FullFence)}, + + {CC "isBigEndian0", CC "()Z", FN_PTR(Unsafe_isBigEndian0)}, ++ {CC "getUseHashMapIntegerCache", CC "()Z", FN_PTR(Unsafe_GetUseHashMapIntegerCache)}, + {CC "getUseFastSerializer", CC "()Z", FN_PTR(Unsafe_GetUseFastSerializer)}, + {CC "unalignedAccess0", CC "()Z", FN_PTR(Unsafe_unalignedAccess0)} + }; +diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp +index 47458b6c1..b8c0bec40 100644 +--- a/src/hotspot/share/runtime/globals.hpp ++++ b/src/hotspot/share/runtime/globals.hpp +@@ -2677,6 +2677,11 @@ define_pd_global(uint64_t,MaxRAM, 1ULL*G); + JFR_ONLY(product(ccstr, StartFlightRecording, NULL, \ + "Start flight recording with options")) \ + \ ++ experimental(bool, UseHashMapIntegerCache, false, \ ++ "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.") \ ++ \ + experimental(bool, UseFastSerializer, false, \ + "Cache-based serialization.It is extremely fast, but it can only" \ + "be effective in certain scenarios.") \ +diff --git a/src/java.base/share/classes/java/util/HashMap.java b/src/java.base/share/classes/java/util/HashMap.java +index df303031a..c260b61fd 100644 +--- a/src/java.base/share/classes/java/util/HashMap.java ++++ b/src/java.base/share/classes/java/util/HashMap.java +@@ -35,6 +35,7 @@ import java.util.function.BiFunction; + import java.util.function.Consumer; + import java.util.function.Function; + import jdk.internal.misc.SharedSecrets; ++import jdk.internal.misc.Unsafe; + + /** + * Hash table based implementation of the {@code Map} interface. This +@@ -272,6 +273,28 @@ public class HashMap extends AbstractMap + */ + 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.) +@@ -298,6 +321,10 @@ public class HashMap extends AbstractMap + } + + 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; +@@ -390,6 +417,12 @@ public class HashMap extends AbstractMap + */ + transient Node[] table; + ++ /** ++ * Cache Value> Map ++ * integerCache[key->intValue] = V ++ */ ++ transient Object[] integerCache; ++ + /** + * Holds cached entrySet(). Note that AbstractMap fields are used + * for keySet() and values(). +@@ -547,7 +580,20 @@ public class HashMap extends AbstractMap + * + * @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 e; + return (e = getNode(hash(key), key)) == null ? null : e.value; + } +@@ -588,6 +634,17 @@ public class HashMap extends AbstractMap + * key. + */ + public boolean containsKey(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 && integerCache[val] != null) { ++ return true; ++ } ++ } ++ } + return getNode(hash(key), key) != null; + } + +@@ -620,6 +677,11 @@ public class HashMap extends AbstractMap + final V putVal(int hash, K key, V value, boolean onlyIfAbsent, + boolean evict) { + Node[] tab; Node 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) +@@ -740,6 +802,8 @@ public class HashMap extends AbstractMap + } + } + } ++ ++ createIntegerCache(); + return newTab; + } + +@@ -839,6 +903,10 @@ public class HashMap extends AbstractMap + p.next = node.next; + ++modCount; + --size; ++ ++ if (integerCache != null) { ++ updateIntegerCache(node.key, null, false); ++ } + afterNodeRemoval(node); + return node; + } +@@ -858,6 +926,7 @@ public class HashMap extends AbstractMap + for (int i = 0; i < tab.length; ++i) + tab[i] = null; + } ++ integerCache = null; + } + + /** +@@ -882,6 +951,82 @@ public class HashMap extends AbstractMap + 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 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> entries = this.entrySet().iterator(); ++ while (entries.hasNext()) { ++ Map.Entry 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 +@@ -1047,7 +1192,19 @@ public class HashMap extends AbstractMap + // 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 e; + return (e = getNode(hash(key), key)) == null ? defaultValue : e.value; + } +@@ -1068,6 +1225,9 @@ public class HashMap extends AbstractMap + if ((e = getNode(hash(key), 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; + } +@@ -1080,6 +1240,9 @@ public class HashMap extends AbstractMap + if ((e = getNode(hash(key), key)) != null) { + V oldValue = e.value; + e.value = value; ++ if (integerCache != null) { ++ updateIntegerCache(key, value, false); ++ } + afterNodeAccess(e); + return oldValue; + } +@@ -1136,6 +1299,9 @@ public class HashMap extends AbstractMap + return null; + } else if (old != null) { + old.value = v; ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } + afterNodeAccess(old); + return v; + } +@@ -1146,6 +1312,11 @@ public class HashMap extends AbstractMap + if (binCount >= TREEIFY_THRESHOLD - 1) + treeifyBin(tab, hash); + } ++ ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } ++ + modCount = mc + 1; + ++size; + afterNodeInsertion(true); +@@ -1176,6 +1347,9 @@ public class HashMap extends AbstractMap + if (mc != modCount) { throw new ConcurrentModificationException(); } + if (v != null) { + e.value = v; ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } + afterNodeAccess(e); + return v; + } +@@ -1230,6 +1404,9 @@ public class HashMap extends AbstractMap + if (old != null) { + if (v != null) { + old.value = v; ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } + afterNodeAccess(old); + } + else +@@ -1243,6 +1420,9 @@ public class HashMap extends AbstractMap + if (binCount >= TREEIFY_THRESHOLD - 1) + treeifyBin(tab, hash); + } ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } + modCount = mc + 1; + ++size; + afterNodeInsertion(true); +@@ -1303,6 +1483,9 @@ public class HashMap extends AbstractMap + } + if (v != null) { + old.value = v; ++ if (integerCache != null) { ++ updateIntegerCache(key, v, false); ++ } + afterNodeAccess(old); + } + else +@@ -1317,6 +1500,9 @@ public class HashMap extends AbstractMap + if (binCount >= TREEIFY_THRESHOLD - 1) + treeifyBin(tab, hash); + } ++ if (integerCache != null) { ++ updateIntegerCache(key, value, false); ++ } + ++modCount; + ++size; + afterNodeInsertion(true); +@@ -1350,6 +1536,9 @@ public class HashMap extends AbstractMap + for (Node 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) +@@ -1823,6 +2012,7 @@ public class HashMap extends AbstractMap + 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 d78caabdc..4d71e671e 100644 +--- a/src/java.base/share/classes/jdk/internal/misc/Unsafe.java ++++ b/src/java.base/share/classes/jdk/internal/misc/Unsafe.java +@@ -3702,7 +3702,7 @@ public final class Unsafe { + private static int convEndian(boolean big, int n) { return big == BE ? n : Integer.reverseBytes(n) ; } + private static long convEndian(boolean big, long n) { return big == BE ? 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.19.1 + diff --git a/java-11-openjdk.spec b/java-11-openjdk.spec index 21c0f13ba0d50b850e52d734158b7336dcac522e..0612d88f9605f2092efd6062d5f84bb5b6c9bba1 100644 --- a/java-11-openjdk.spec +++ b/java-11-openjdk.spec @@ -735,7 +735,7 @@ Provides: java-src%{?1} = %{epoch}:%{version}-%{release} Name: java-%{javaver}-%{origin} Version: %{newjavaver}.%{buildver} -Release: 8 +Release: 9 # java-1.5.0-ibm from jpackage.org set Epoch to 1 for unknown reasons # and this change was brought into RHEL-4. java-1.5.0-ibm packages # also included the epoch in their virtual provides. This created a @@ -840,7 +840,8 @@ Patch53: 8236512-PKCS11-Connection-closed-after-Cipher-doFinal-and-NoPadding.pat Patch54: 8207160-ClassReader-adjustMethodParams-can-potentially-return-null-if-the-args-list-is-empty.patch Patch55: 8215047-Task-terminators-do-not-complete-termination-in-consistent-state.patch Patch56: 8247766-aarch64-guarantee-val-1U--nbits-failed-Field-too-big-for-insn.patch -Patch57: add-zgc-parameter-adaptation-feature.patch +Patch57: add-zgc-parameter-adaptation-feature.patch +Patch58: add-integerCache-feature.patch BuildRequires: autoconf BuildRequires: alsa-lib-devel @@ -1111,6 +1112,7 @@ pushd %{top_level_dir_name} %patch55 -p1 %patch56 -p1 %patch57 -p1 +%patch58 -p1 popd # openjdk %patch1000 @@ -1613,6 +1615,9 @@ require "copy_jdk_configs.lua" %changelog +* Thu Dec 24 2020 kuenking - 1:11.0.9.11-9 +- add add-integerCache-feature.patch + * Thu Dec 24 2020 kuenking - 1:11.0.9.11-8 - add add-zgc-parameter-adaptation-feature.patch