8209120: Archive the Integer.IntegerCache
Reviewed-by: jiangli, alanb, plevart, iklam, mchung
--- a/src/hotspot/share/classfile/javaClasses.cpp Fri Aug 10 09:16:10 2018 +0200
+++ b/src/hotspot/share/classfile/javaClasses.cpp Mon Aug 13 19:21:43 2018 +0200
@@ -4251,6 +4251,7 @@
int jdk_internal_module_ArchivedModuleGraph::_archivedModuleFinder_offset;
int jdk_internal_module_ArchivedModuleGraph::_archivedMainModule_offset;
int jdk_internal_module_ArchivedModuleGraph::_archivedConfiguration_offset;
+int java_lang_Integer_IntegerCache::_archivedCache_offset;
int java_lang_module_Configuration::_EMPTY_CONFIGURATION_offset;
int java_util_ImmutableCollections_ListN::_EMPTY_LIST_offset;
int java_util_ImmutableCollections_SetN::_EMPTY_SET_offset;
@@ -4417,6 +4418,21 @@
return (hardcoded_offset * heapOopSize) + instanceOopDesc::base_offset_in_bytes();
}
+#define INTEGERCACHE_FIELDS_DO(macro) \
+ macro(_archivedCache_offset, k, "archivedCache", java_lang_Integer_array_signature, true)
+
+void java_lang_Integer_IntegerCache::compute_offsets() {
+ InstanceKlass* k = SystemDictionary::Integer_IntegerCache_klass();
+ assert(k != NULL, "must be loaded");
+ INTEGERCACHE_FIELDS_DO(FIELD_COMPUTE_OFFSET);
+}
+
+#if INCLUDE_CDS
+void java_lang_Integer_IntegerCache::serialize(SerializeClosure* f) {
+ INTEGERCACHE_FIELDS_DO(FIELD_SERIALIZE_OFFSET);
+}
+#endif
+
#define ARCHIVEDMODULEGRAPH_FIELDS_DO(macro) \
macro(_archivedSystemModules_offset, k, "archivedSystemModules", systemModules_signature, true); \
macro(_archivedModuleFinder_offset, k, "archivedModuleFinder", moduleFinder_signature, true); \
@@ -4553,6 +4569,7 @@
java_lang_LiveStackFrameInfo::compute_offsets();
java_util_concurrent_locks_AbstractOwnableSynchronizer::compute_offsets();
+ java_lang_Integer_IntegerCache::compute_offsets();
java_lang_module_Configuration::compute_offsets();
java_util_ImmutableCollections_ListN::compute_offsets();
java_util_ImmutableCollections_MapN::compute_offsets();
--- a/src/hotspot/share/classfile/javaClasses.hpp Fri Aug 10 09:16:10 2018 +0200
+++ b/src/hotspot/share/classfile/javaClasses.hpp Mon Aug 13 19:21:43 2018 +0200
@@ -1485,6 +1485,15 @@
static void serialize(SerializeClosure* f) NOT_CDS_RETURN;
};
+class java_lang_Integer_IntegerCache: AllStatic {
+ private:
+ static int _archivedCache_offset;
+ public:
+ static int archivedCache_offset() { return _archivedCache_offset; }
+ static void compute_offsets();
+ static void serialize(SerializeClosure* f) NOT_CDS_RETURN;
+};
+
class jdk_internal_module_ArchivedModuleGraph: AllStatic {
private:
static int _archivedSystemModules_offset;
--- a/src/hotspot/share/classfile/systemDictionary.hpp Fri Aug 10 09:16:10 2018 +0200
+++ b/src/hotspot/share/classfile/systemDictionary.hpp Mon Aug 13 19:21:43 2018 +0200
@@ -215,6 +215,7 @@
do_klass(Byte_klass, java_lang_Byte, Pre ) \
do_klass(Short_klass, java_lang_Short, Pre ) \
do_klass(Integer_klass, java_lang_Integer, Pre ) \
+ do_klass(Integer_IntegerCache_klass, java_lang_Integer_IntegerCache, Pre ) \
do_klass(Long_klass, java_lang_Long, Pre ) \
\
/* JVMCI classes. These are loaded on-demand. */ \
--- a/src/hotspot/share/classfile/vmSymbols.hpp Fri Aug 10 09:16:10 2018 +0200
+++ b/src/hotspot/share/classfile/vmSymbols.hpp Mon Aug 13 19:21:43 2018 +0200
@@ -438,6 +438,7 @@
template(fileToEncodedURL_signature, "(Ljava/io/File;)Ljava/net/URL;") \
template(getProtectionDomain_name, "getProtectionDomain") \
template(getProtectionDomain_signature, "(Ljava/security/CodeSource;)Ljava/security/ProtectionDomain;") \
+ template(java_lang_Integer_array_signature, "[Ljava/lang/Integer;") \
template(url_code_signer_array_void_signature, "(Ljava/net/URL;[Ljava/security/CodeSigner;)V") \
template(module_entry_name, "module_entry") \
template(resolved_references_name, "<resolved_references>") \
--- a/src/hotspot/share/memory/heapShared.cpp Fri Aug 10 09:16:10 2018 +0200
+++ b/src/hotspot/share/memory/heapShared.cpp Mon Aug 13 19:21:43 2018 +0200
@@ -510,6 +510,7 @@
archive_object_graph_do(SystemDictionary::ImmutableCollections_ListN_klass(), java_util_ImmutableCollections_ListN::EMPTY_LIST_offset(), T_OBJECT, CHECK); \
archive_object_graph_do(SystemDictionary::ImmutableCollections_MapN_klass(), java_util_ImmutableCollections_MapN::EMPTY_MAP_offset(), T_OBJECT, CHECK); \
archive_object_graph_do(SystemDictionary::ImmutableCollections_SetN_klass(), java_util_ImmutableCollections_SetN::EMPTY_SET_offset(), T_OBJECT, CHECK); \
+ archive_object_graph_do(SystemDictionary::Integer_IntegerCache_klass(), java_lang_Integer_IntegerCache::archivedCache_offset(), T_OBJECT, CHECK); \
archive_object_graph_do(SystemDictionary::Configuration_klass(), java_lang_module_Configuration::EMPTY_CONFIGURATION_offset(), T_OBJECT, CHECK)
void HeapShared::archive_module_graph_objects(Thread* THREAD) {
--- a/src/java.base/share/classes/java/lang/Integer.java Fri Aug 10 09:16:10 2018 +0200
+++ b/src/java.base/share/classes/java/lang/Integer.java Mon Aug 13 19:21:43 2018 +0200
@@ -997,7 +997,8 @@
private static class IntegerCache {
static final int low = -128;
static final int high;
- static final Integer cache[];
+ static final Integer[] cache;
+ static Integer[] archivedCache;
static {
// high value may be configured by property
@@ -1016,11 +1017,19 @@
}
high = h;
- cache = new Integer[(high - low) + 1];
- int j = low;
- for(int k = 0; k < cache.length; k++)
- cache[k] = new Integer(j++);
+ // Load IntegerCache.archivedCache from archive, if possible
+ VM.initializeFromArchive(IntegerCache.class);
+ int size = (high - low) + 1;
+ // Use the archived cache if it exists and is large enough
+ if (archivedCache == null || size > archivedCache.length) {
+ Integer[] c = new Integer[size];
+ int j = low;
+ for(int k = 0; k < c.length; k++)
+ c[k] = new Integer(j++);
+ archivedCache = c;
+ }
+ cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/ArchivedIntegerCacheTest.java Mon Aug 13 19:21:43 2018 +0200
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * 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,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+/*
+ * @test
+ * @summary Test IntegerCache integrity in various scenarios
+ * @requires vm.cds.archived.java.heap
+ * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/appcds
+ * @modules java.base/jdk.internal.misc
+ * java.management
+ * jdk.jartool/sun.tools.jar
+ * @build sun.hotspot.WhiteBox
+ * @compile CheckIntegerCacheApp.java
+ * @run driver ClassFileInstaller -jar integer.jar CheckIntegerCacheApp
+ * @run driver ClassFileInstaller -jar WhiteBox.jar sun.hotspot.WhiteBox
+ * @run main ArchivedIntegerCacheTest
+ */
+
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import jdk.test.lib.process.OutputAnalyzer;
+
+public class ArchivedIntegerCacheTest {
+
+ public static void main(String[] args) throws Exception {
+ String wbJar = ClassFileInstaller.getJarPath("WhiteBox.jar");
+ String use_whitebox_jar = "-Xbootclasspath/a:" + wbJar;
+ String appJar = ClassFileInstaller.getJarPath("integer.jar");
+
+ Path userDir = Paths.get(System.getProperty("user.dir"));
+ Path moduleDir = Files.createTempDirectory(userDir, "mods");
+
+ //
+ // Dump default archive
+ //
+ OutputAnalyzer output = TestCommon.dump(appJar,
+ TestCommon.list("CheckIntegerCacheApp"),
+ use_whitebox_jar);
+ TestCommon.checkDump(output);
+
+ // Test case 1)
+ // - Default options
+ System.out.println("----------------------- Test case 1 ----------------------");
+ output = TestCommon.exec(appJar, use_whitebox_jar,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "CheckIntegerCacheApp",
+ "127",
+ "true");
+ TestCommon.checkExec(output);
+
+ // Test case 2)
+ // - Default archive
+ // - Larger -XX:AutoBoxCacheMax
+ System.out.println("----------------------- Test case 2 ----------------------");
+ output = TestCommon.exec(appJar, use_whitebox_jar,
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "-XX:AutoBoxCacheMax=20000",
+ "CheckIntegerCacheApp",
+ "20000",
+ "false");
+ TestCommon.checkExec(output);
+
+ //
+ // Dump with -XX:AutoBoxCacheMax specified
+ //
+ output = TestCommon.dump(appJar,
+ TestCommon.list("CheckIntegerCacheApp"),
+ "-XX:AutoBoxCacheMax=20000",
+ use_whitebox_jar);
+ TestCommon.checkDump(output);
+
+ // Test case 3)
+ // - Large archived cache
+ // - Default options
+ System.out.println("----------------------- Test case 3 ----------------------");
+ output = TestCommon.exec(appJar, use_whitebox_jar,
+ "--module-path",
+ moduleDir.toString(),
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "CheckIntegerCacheApp",
+ "127",
+ "true");
+ TestCommon.checkExec(output);
+
+
+ // Test case 4)
+ // - Large archived cache
+ // - Matching options
+ System.out.println("----------------------- Test case 4 ----------------------");
+ output = TestCommon.exec(appJar, use_whitebox_jar,
+ "--module-path",
+ moduleDir.toString(),
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "-XX:AutoBoxCacheMax=20000",
+ "CheckIntegerCacheApp",
+ "20000",
+ "true");
+ TestCommon.checkExec(output);
+
+ // Test case 5)
+ // - Large archived cache
+ // - Larger requested cache
+ System.out.println("----------------------- Test case 5 ----------------------");
+ output = TestCommon.exec(appJar, use_whitebox_jar,
+ "--module-path",
+ moduleDir.toString(),
+ "-XX:+UnlockDiagnosticVMOptions",
+ "-XX:+WhiteBoxAPI",
+ "-XX:AutoBoxCacheMax=30000",
+ "CheckIntegerCacheApp",
+ "30000",
+ "false");
+ TestCommon.checkExec(output);
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckIntegerCacheApp.java Mon Aug 13 19:21:43 2018 +0200
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * 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,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ *
+ */
+
+import sun.hotspot.WhiteBox;
+
+//
+// Help test archived integer cache consistency.
+//
+// Takes two arguments:
+// 0: the expected AutoBoxCacheMax setting
+// 1: if the values are expected to be retrieved from the archive or not
+//
+public class CheckIntegerCacheApp {
+ static WhiteBox wb;
+
+ public static void main(String[] args) throws Exception {
+ wb = WhiteBox.getWhiteBox();
+
+ if (!wb.areOpenArchiveHeapObjectsMapped()) {
+ System.out.println("This may happen during normal operation. Test Skipped.");
+ return;
+ }
+
+ if (args.length != 2) {
+ throw new RuntimeException(
+ "FAILED. Incorrect argument length: " + args.length);
+ }
+
+ boolean archivedExpected = Boolean.parseBoolean(args[1]);
+
+ // Base JLS compliance check
+ for (int i = -128; i <= 127; i++) {
+ if (Integer.valueOf(i) != Integer.valueOf(i)) {
+ throw new RuntimeException(
+ "FAILED. All values in range [-128, 127] should be interned in cache: " + i);
+ }
+ checkArchivedAsExpected(archivedExpected, i);
+ }
+
+ int high = Integer.parseInt(args[0]);
+ if (Integer.valueOf(high) != Integer.valueOf(high)) {
+ throw new RuntimeException(
+ "FAILED. Value expected to be retrieved from cache: " + high);
+ }
+ checkArchivedAsExpected(archivedExpected, Integer.valueOf(high));
+
+ if (Integer.valueOf(high + 1) == Integer.valueOf(high + 1)) {
+ throw new RuntimeException(
+ "FAILED. Value not expected to be retrieved from cache: " + high);
+ }
+ checkArchivedAsExpected(false, Integer.valueOf(high + 1));
+ }
+
+ private static void checkArchivedAsExpected(boolean archivedExpected, Integer value) {
+ if (archivedExpected) {
+ if (!wb.isShared(Integer.valueOf(value))) {
+ throw new RuntimeException(
+ "FAILED. Value expected to be archived: " + value);
+ }
+ } else {
+ if (wb.isShared(Integer.valueOf(value))) {
+ throw new RuntimeException(
+ "FAILED. Value not expected to be archived: " + value);
+ }
+ }
+ }
+}