8209120: Archive the Integer.IntegerCache
authorredestad
Mon, 13 Aug 2018 19:21:43 +0200
changeset 51388 0dcd27526967
parent 51387 9fe4fc157f4e
child 51389 ae001a1deb74
8209120: Archive the Integer.IntegerCache Reviewed-by: jiangli, alanb, plevart, iklam, mchung
src/hotspot/share/classfile/javaClasses.cpp
src/hotspot/share/classfile/javaClasses.hpp
src/hotspot/share/classfile/systemDictionary.hpp
src/hotspot/share/classfile/vmSymbols.hpp
src/hotspot/share/memory/heapShared.cpp
src/java.base/share/classes/java/lang/Integer.java
test/hotspot/jtreg/runtime/appcds/cacheObject/ArchivedIntegerCacheTest.java
test/hotspot/jtreg/runtime/appcds/cacheObject/CheckIntegerCacheApp.java
--- 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);
+            }
+        }
+    }
+}