diff --git a/src/hotspot/share/cds/aotMetaspace.cpp b/src/hotspot/share/cds/aotMetaspace.cpp index 4c23ede9cb8..76634fc3fba 100644 --- a/src/hotspot/share/cds/aotMetaspace.cpp +++ b/src/hotspot/share/cds/aotMetaspace.cpp @@ -999,7 +999,23 @@ void AOTMetaspace::dump_static_archive(TRAPS) { } #if INCLUDE_CDS_JAVA_HEAP && defined(_LP64) -void AOTMetaspace::adjust_heap_sizes_for_dumping() { +void AOTMetaspace::init_heap_settings() { + if (UseCompressedOops) { + if (!AOTCodeCache::is_caching_enabled()) { + // We don't need it -- always disable for better jitted code. + FLAG_SET_ERGO(AOTCompatibleOopCompression, false); + } else if (CDSConfig::is_dumping_final_static_archive()) { + // Obey the command-line switch. Do not override + } else if (CDSConfig::is_using_archive()) { + precond(FileMapInfo::current_info() == nullptr); + FileMapInfo* static_mapinfo = open_static_archive(); + if (static_mapinfo != nullptr && static_mapinfo->header()->compatible_oop_compression()) { + // Use the same setting as recorded in the archive. + FLAG_SET_ERGO(AOTCompatibleOopCompression, true); + } + } + } + if (!CDSConfig::is_dumping_heap() || UseCompressedOops) { return; } @@ -1502,7 +1518,10 @@ void AOTMetaspace::initialize_runtime_shared_and_meta_spaces() { assert(CDSConfig::is_using_archive(), "Must be called when UseSharedSpaces is enabled"); MapArchiveResult result = MAP_ARCHIVE_OTHER_FAILURE; - FileMapInfo* static_mapinfo = open_static_archive(); + FileMapInfo* static_mapinfo = FileMapInfo::current_info(); // may have been opened by init_heap_settings() + if (static_mapinfo == nullptr) { + static_mapinfo = open_static_archive(); + } FileMapInfo* dynamic_mapinfo = nullptr; if (static_mapinfo != nullptr) { diff --git a/src/hotspot/share/cds/aotMetaspace.hpp b/src/hotspot/share/cds/aotMetaspace.hpp index 2236bae91f3..975b6be76d7 100644 --- a/src/hotspot/share/cds/aotMetaspace.hpp +++ b/src/hotspot/share/cds/aotMetaspace.hpp @@ -77,7 +77,7 @@ class AOTMetaspace : AllStatic { static void dump_static_archive(TRAPS) NOT_CDS_RETURN; #ifdef _LP64 - static void adjust_heap_sizes_for_dumping() NOT_CDS_JAVA_HEAP_RETURN; + static void init_heap_settings() NOT_CDS_JAVA_HEAP_RETURN; #endif private: diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 1ed9979ff85..1c136ac61a8 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -225,6 +225,7 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, } #endif _compressed_oops = UseCompressedOops; + _compatible_oop_compression = AOTCompatibleOopCompression; _narrow_klass_pointer_bits = CompressedKlassPointers::narrow_klass_pointer_bits(); _narrow_klass_shift = ArchiveBuilder::precomputed_narrow_klass_shift(); @@ -1339,6 +1340,10 @@ bool FileMapInfo::map_aot_code_region(ReservedSpace rs) { FileMapRegion* r = region_at(AOTMetaspace::ac); assert(r->used() > 0 && r->used_aligned() == rs.size(), "must be"); + if (UseCompressedOops) { + precond(header()->compatible_oop_compression() == AOTCompatibleOopCompression); + } + char* requested_base = rs.base(); assert(requested_base != nullptr, "should be inside code cache"); @@ -1592,6 +1597,7 @@ bool FileMapInfo::can_use_heap_region() { if (UseCompressedOops) { aot_log_info(aot)(" narrow_oop_mode = %d, narrow_oop_base = " PTR_FORMAT ", narrow_oop_shift = %d", narrow_oop_mode(), p2i(narrow_oop_base()), narrow_oop_shift()); + aot_log_info(aot)(" AOTCompatibleOopCompression = %s", header()->compatible_oop_compression() ? "true" : "false"); } aot_log_info(aot)("The current max heap size = %zuM, G1HeapRegion::GrainBytes = %zu", MaxHeapSize/M, G1HeapRegion::GrainBytes); @@ -1600,6 +1606,7 @@ bool FileMapInfo::can_use_heap_region() { if (UseCompressedOops) { aot_log_info(aot)(" narrow_oop_mode = %d, narrow_oop_base = " PTR_FORMAT ", narrow_oop_shift = %d", CompressedOops::mode(), p2i(CompressedOops::base()), CompressedOops::shift()); + aot_log_info(aot)(" AOTCompatibleOopCompression = %s", AOTCompatibleOopCompression ? "true" : "false"); } if (!object_streaming_mode()) { aot_log_info(aot)(" heap range = [" PTR_FORMAT " - " PTR_FORMAT "]", diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index bae08bd5bc7..4ade7d03b65 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -120,6 +120,7 @@ private: CompressedOops::Mode _narrow_oop_mode; // compressed oop encoding mode bool _object_streaming_mode; // dump was created for object streaming bool _compressed_oops; // save the flag UseCompressedOops + bool _compatible_oop_compression; // value of AOTCompatibleOopCompression at dump time int _narrow_klass_pointer_bits; // save number of bits in narrowKlass int _narrow_klass_shift; // save shift width used to pre-compute narrowKlass IDs in archived heap objects narrowPtr _cloned_vtables; // The address of the first cloned vtable @@ -199,6 +200,7 @@ public: bool has_platform_or_app_classes() const { return _has_platform_or_app_classes; } bool has_aot_linked_classes() const { return _has_aot_linked_classes; } bool compressed_oops() const { return _compressed_oops; } + bool compatible_oop_compression() const { return _compatible_oop_compression; } int narrow_klass_pointer_bits() const { return _narrow_klass_pointer_bits; } int narrow_klass_shift() const { return _narrow_klass_shift; } bool has_full_module_graph() const { return _has_full_module_graph; } diff --git a/src/hotspot/share/code/aotCodeCache.cpp b/src/hotspot/share/code/aotCodeCache.cpp index b330ed35d0b..0e4aea0806b 100644 --- a/src/hotspot/share/code/aotCodeCache.cpp +++ b/src/hotspot/share/code/aotCodeCache.cpp @@ -465,6 +465,7 @@ void AOTCodeCache::Config::record(uint cpu_features_offset) { // Special configs that cannot be checked with macros _compressedOopBase = CompressedOops::base(); + _compressedOopShift = CompressedOops::shift(); #if defined(X86) && !defined(ZERO) _useUnalignedLoadStores = UseUnalignedLoadStores; @@ -577,10 +578,17 @@ bool AOTCodeCache::Config::verify(AOTCodeCache* cache) const { AOTCODECACHE_CONFIGS_DO(AOTCODECACHE_CHECK_VAR, AOTCODECACHE_CHECK_FUN); // Special configs that cannot be checked with macros +#define COMPRESSED_OOPS_HINT "Consider adding -XX:+AOTCompatibleOopCompression when creating the AOT cache" if ((_compressedOopBase == nullptr || CompressedOops::base() == nullptr) && (_compressedOopBase != CompressedOops::base())) { load_failure_log().print_cr("AOT Code Cache disabled: incompatible CompressedOops::base(): %p vs current %p", _compressedOopBase, CompressedOops::base()); + load_failure_log().print_cr(COMPRESSED_OOPS_HINT); + return false; + } + + if (!check_config(_compressedOopShift, CompressedOops::shift(), "CompressedOops::shift()")) { + load_failure_log().print_cr(COMPRESSED_OOPS_HINT); return false; } diff --git a/src/hotspot/share/code/aotCodeCache.hpp b/src/hotspot/share/code/aotCodeCache.hpp index 039735dc954..77fb90b955b 100644 --- a/src/hotspot/share/code/aotCodeCache.hpp +++ b/src/hotspot/share/code/aotCodeCache.hpp @@ -301,7 +301,6 @@ public: do_var(bool, UseSHA512Intrinsics) \ do_var(bool, UseVectorizedMismatchIntrinsic) \ do_fun(int, CompressedKlassPointers_shift, CompressedKlassPointers::shift()) \ - do_fun(int, CompressedOops_shift, CompressedOops::shift()) \ do_fun(bool, JavaAssertions_systemClassDefault, JavaAssertions::systemClassDefault()) \ do_fun(bool, JavaAssertions_userClassDefault, JavaAssertions::userClassDefault()) \ do_fun(CollectedHeap::Name, Universe_heap_kind, Universe::heap()->kind()) \ @@ -377,6 +376,7 @@ protected: // Special configs that cannot be checked with macros address _compressedOopBase; + int _compressedOopShift; #if defined(X86) && !defined(ZERO) bool _useUnalignedLoadStores; diff --git a/src/hotspot/share/memory/memoryReserver.cpp b/src/hotspot/share/memory/memoryReserver.cpp index f9359420693..aaee78ab492 100644 --- a/src/hotspot/share/memory/memoryReserver.cpp +++ b/src/hotspot/share/memory/memoryReserver.cpp @@ -488,7 +488,8 @@ static ReservedSpace establish_noaccess_prefix(const ReservedSpace& reserved, si assert(reserved.alignment() >= os::vm_page_size(), "must be at least page size big"); assert(reserved.is_reserved(), "should only be called on a reserved memory area"); - if (reserved.end() > (char *)OopEncodingHeapMax) { + if (reserved.end() > (char *)OopEncodingHeapMax || AOTCompatibleOopCompression) { + assert((reserved.base() != nullptr), "sanity"); if (true WIN64_ONLY(&& !UseLargePages) AIX_ONLY(&& (os::Aix::supports_64K_mmap_pages() || os::vm_page_size() == 4*K))) { @@ -534,13 +535,20 @@ ReservedHeapSpace HeapReserver::Instance::reserve_compressed_oops_heap(const siz const size_t attach_point_alignment = lcm(alignment, os_attach_point_alignment); uintptr_t aligned_heap_base_min_address = align_up(MAX2(HeapBaseMinAddress, alignment), alignment); - size_t noaccess_prefix = ((aligned_heap_base_min_address + size) > OopEncodingHeapMax) ? - noaccess_prefix_size : 0; + uintptr_t heap_end_address = aligned_heap_base_min_address + size; + + bool unscaled = false; + bool zerobased = false; + if (!AOTCompatibleOopCompression) { // heap base is not enforced + unscaled = (heap_end_address <= UnscaledOopHeapMax); + zerobased = (heap_end_address <= OopEncodingHeapMax); + } + size_t noaccess_prefix = !zerobased ? noaccess_prefix_size : 0; ReservedSpace reserved{}; // Attempt to alloc at user-given address. - if (!FLAG_IS_DEFAULT(HeapBaseMinAddress)) { + if (!FLAG_IS_DEFAULT(HeapBaseMinAddress) || AOTCompatibleOopCompression) { reserved = try_reserve_memory(size + noaccess_prefix, alignment, page_size, (char*)aligned_heap_base_min_address); if (reserved.base() != (char*)aligned_heap_base_min_address) { // Enforce this exact address. release(reserved); @@ -562,7 +570,7 @@ ReservedHeapSpace HeapReserver::Instance::reserve_compressed_oops_heap(const siz // Attempt to allocate so that we can run without base and scale (32-Bit unscaled compressed oops). // Give it several tries from top of range to bottom. - if (aligned_heap_base_min_address + size <= UnscaledOopHeapMax) { + if (unscaled) { // Calc address range within we try to attach (range of possible start addresses). uintptr_t const highest_start = align_down(UnscaledOopHeapMax - size, attach_point_alignment); @@ -577,7 +585,7 @@ ReservedHeapSpace HeapReserver::Instance::reserve_compressed_oops_heap(const siz const uintptr_t zerobased_max = OopEncodingHeapMax; // Give it several tries from top of range to bottom. - if (aligned_heap_base_min_address + size <= zerobased_max && // Zerobased theoretical possible. + if (zerobased && // Zerobased theoretical possible. ((!reserved.is_reserved()) || // No previous try succeeded. (reserved.end() > (char*)zerobased_max))) { // Unscaled delivered an arbitrary address. @@ -646,6 +654,7 @@ ReservedHeapSpace HeapReserver::Instance::reserve_compressed_oops_heap(const siz } // We reserved heap memory without a noaccess prefix. + assert(!AOTCompatibleOopCompression, "noaccess prefix is missing"); return ReservedHeapSpace(reserved, 0 /* noaccess_prefix */); } diff --git a/src/hotspot/share/memory/universe.cpp b/src/hotspot/share/memory/universe.cpp index f83f1b1786f..2adf211304c 100644 --- a/src/hotspot/share/memory/universe.cpp +++ b/src/hotspot/share/memory/universe.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2026, 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 @@ -885,7 +885,7 @@ jint universe_init() { ObjLayout::initialize(); #ifdef _LP64 - AOTMetaspace::adjust_heap_sizes_for_dumping(); + AOTMetaspace::init_heap_settings(); #endif // _LP64 GCConfig::arguments()->initialize_heap_sizes(); diff --git a/src/hotspot/share/oops/compressedOops.cpp b/src/hotspot/share/oops/compressedOops.cpp index b09aaa3547d..92b7b2b3081 100644 --- a/src/hotspot/share/oops/compressedOops.cpp +++ b/src/hotspot/share/oops/compressedOops.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2019, 2026, 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 @@ -54,11 +54,11 @@ void CompressedOops::initialize(const ReservedHeapSpace& heap_space) { // See needs_explicit_null_check. // Only set the heap base for compressed oops because it indicates // compressed oops for pstack code. - if ((uint64_t)heap_space.end() > UnscaledOopHeapMax) { + if ((uint64_t)heap_space.end() > UnscaledOopHeapMax || AOTCompatibleOopCompression) { // Didn't reserve heap below 4Gb. Must shift. set_shift(LogMinObjAlignmentInBytes); } - if ((uint64_t)heap_space.end() <= OopEncodingHeapMax) { + if ((uint64_t)heap_space.end() <= OopEncodingHeapMax && !AOTCompatibleOopCompression) { // Did reserve heap below 32Gb. Can use base == 0; set_base(nullptr); } else { diff --git a/src/hotspot/share/runtime/globals.hpp b/src/hotspot/share/runtime/globals.hpp index cd10a0174fa..ed4a3a9a44b 100644 --- a/src/hotspot/share/runtime/globals.hpp +++ b/src/hotspot/share/runtime/globals.hpp @@ -124,6 +124,10 @@ const size_t minimumSymbolTableSize = 1024; "Use 32-bit object references in 64-bit VM. " \ "lp64_product means flag is always constant in 32 bit VM") \ \ + product(bool, AOTCompatibleOopCompression, false, DIAGNOSTIC, \ + "Always use HeapBasedNarrowOop mode, so that AOT code will " \ + "always work regardless of runtime heap range") \ + \ product(bool, UseCompactObjectHeaders, false, \ "Use compact 64-bit object headers in 64-bit VM") \ \ @@ -142,6 +146,7 @@ const size_t minimumSymbolTableSize = 1024; range, \ constraint) const bool UseCompressedOops = false; +const bool AOTCompatibleOopCompression = false; const bool UseCompactObjectHeaders = false; const int ObjectAlignmentInBytes = 8; diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompatibleOopCompression.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompatibleOopCompression.java new file mode 100644 index 00000000000..7d557a3b1c7 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCache/AOTCompatibleOopCompression.java @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2026, 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 Sanity test for -XX:+AOTCompatibleOopCompression + * @requires vm.cds.supports.aot.class.linking + * @requires vm.bits == 64 & vm.opt.final.UseCompressedOops == true + * @library /test/lib + * @build AOTCompatibleOopCompression + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar app.jar AOTCompatibleOopCompressionApp + * @run driver AOTCompatibleOopCompression AOT + */ + +import jdk.test.lib.cds.CDSAppTester; +import jdk.test.lib.helpers.ClassFileInstaller; +import jdk.test.lib.process.OutputAnalyzer; + +public class AOTCompatibleOopCompression { + static final String appJar = ClassFileInstaller.getJarPath("app.jar"); + static final String mainClass = AOTCompatibleOopCompressionApp.class.getName(); + + public static void main(String[] args) throws Exception { + Tester tester = new Tester(); + tester.run(args); + + // Since the AOT cache has been assembled with -XX:+AOTCompatibleOopCompression, + // production runs will always run with -XX:+AOTCompatibleOopCompression. The value + // specified in the command-line is ignored. + tester.productionRun(new String[] {"-XX:+UnlockDiagnosticVMOptions", "-XX:-AOTCompatibleOopCompression"}); + tester.productionRun(new String[] {"-XX:+UnlockDiagnosticVMOptions", "-XX:+AOTCompatibleOopCompression"}); + } + + static class Tester extends CDSAppTester { + public Tester() { + super(mainClass); + } + + @Override + public String[] vmArgs(RunMode runMode) { + if (runMode == RunMode.ASSEMBLY) { + return new String[] {"-XX:+UnlockDiagnosticVMOptions", "-XX:+AOTCompatibleOopCompression"}; + } else if (runMode == RunMode.PRODUCTION) { + return new String[] {"-Xlog:aot", "-XX:AOTMode=on", "-XX:HeapBaseMinAddress=0x800000000"}; + } else { + return new String[0]; + } + } + + @Override + public String classpath(RunMode runMode) { + return appJar; + } + + @Override + public String[] appCommandLine(RunMode runMode) { + return new String[] { mainClass }; + } + + @Override + public void checkExecution(OutputAnalyzer out, RunMode runMode) { + if (runMode == RunMode.PRODUCTION) { + out.shouldContain("HelloWorld"); + out.shouldContain("AOTCompatibleOopCompression = true"); + out.shouldNotContain("AOTCompatibleOopCompression = false"); + } + } + } +} + +class AOTCompatibleOopCompressionApp { + public static void main(String[] args) { + System.out.println("HelloWorld"); + } +} diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java index 9ca8ba9ed91..aea4f7e9f8f 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotCode/AOTCodeCompressedOopsTest.java @@ -165,10 +165,12 @@ public class AOTCodeCompressedOopsTest { * [0.022s][info][cds] CDS archive was created with max heap size = 1024M, and the following configuration: * [0.022s][info][cds] narrow_klass_base at mapping start address, narrow_klass_pointer_bits = 32, narrow_klass_shift = 0 * [0.022s][info][cds] narrow_oop_mode = 1, narrow_oop_base = 0x0000000000000000, narrow_oop_shift = 3 + * [0.022s][info][cds] AOTCompatibleOopCompression = false * [0.022s][info][cds] The current max heap size = 31744M, G1HeapRegion::GrainBytes = 16777216 * [0.022s][info][cds] narrow_klass_base = 0x000007fc00000000, arrow_klass_pointer_bits = 32, narrow_klass_shift = 0 * [0.022s][info][cds] narrow_oop_mode = 3, narrow_oop_base = 0x0000000300000000, narrow_oop_shift = 3 * [0.022s][info][cds] heap range = [0x0000000301000000 - 0x0000000ac1000000] + * [0.022s][info][cds] AOTCompatibleOopCompression = false */ Pattern p = Pattern.compile("narrow_oop_base = 0x([0-9a-fA-F]+), narrow_oop_shift = (\\d)"); for (int i = 0; i < list.size(); i++) { @@ -183,7 +185,7 @@ public class AOTCodeCompressedOopsTest { aotCacheBase = Long.valueOf(m.group(1), 16); aotCacheShift = Integer.valueOf(m.group(2)); // Parse current CompressedOops settings - line = list.get(i+5); + line = list.get(i+6); m = p.matcher(line); if (!m.find()) { throw new RuntimeException("Pattern \"" + p + "\" not found in the output");