# HG changeset patch # User redestad # Date 1534168964 -7200 # Mon Aug 13 16:02:44 2018 +0200 # Node ID de5fddea7bbd033c62746974f09d09bfa09847b9 # Parent aed534740851a96d92a3fdadfb32359ebcd2c60c 8209120: Archive the Integer.IntegerCache Reviewed-by: jiangli, alanb, plevart, iklam, mchung diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -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(); diff --git a/src/hotspot/share/classfile/javaClasses.hpp b/src/hotspot/share/classfile/javaClasses.hpp --- a/src/hotspot/share/classfile/javaClasses.hpp +++ b/src/hotspot/share/classfile/javaClasses.hpp @@ -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; diff --git a/src/hotspot/share/classfile/systemDictionary.hpp b/src/hotspot/share/classfile/systemDictionary.hpp --- a/src/hotspot/share/classfile/systemDictionary.hpp +++ b/src/hotspot/share/classfile/systemDictionary.hpp @@ -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. */ \ diff --git a/src/hotspot/share/classfile/vmSymbols.hpp b/src/hotspot/share/classfile/vmSymbols.hpp --- a/src/hotspot/share/classfile/vmSymbols.hpp +++ b/src/hotspot/share/classfile/vmSymbols.hpp @@ -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, "") \ diff --git a/src/hotspot/share/memory/heapShared.cpp b/src/hotspot/share/memory/heapShared.cpp --- a/src/hotspot/share/memory/heapShared.cpp +++ b/src/hotspot/share/memory/heapShared.cpp @@ -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) { diff --git a/src/java.base/share/classes/java/lang/Integer.java b/src/java.base/share/classes/java/lang/Integer.java --- a/src/java.base/share/classes/java/lang/Integer.java +++ b/src/java.base/share/classes/java/lang/Integer.java @@ -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; } diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/ArchivedIntegerCacheTest.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/ArchivedIntegerCacheTest.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/ArchivedIntegerCacheTest.java @@ -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); + } +} diff --git a/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckIntegerCacheApp.java b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckIntegerCacheApp.java new file mode 100644 --- /dev/null +++ b/test/hotspot/jtreg/runtime/appcds/cacheObject/CheckIntegerCacheApp.java @@ -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); + } + } + } +}