diff --git a/src/hotspot/share/cds/aotMapLogger.cpp b/src/hotspot/share/cds/aotMapLogger.cpp index 151c15048c28e..f55ed25016463 100644 --- a/src/hotspot/share/cds/aotMapLogger.cpp +++ b/src/hotspot/share/cds/aotMapLogger.cpp @@ -834,9 +834,9 @@ void AOTMapLogger::dumptime_log_heap_region(ArchiveHeapInfo* heap_info) { address buffer_start = address(r.start()); // start of the current oop inside the buffer address buffer_end = address(r.end()); - address requested_base = UseCompressedOops ? (address)CompressedOops::base() : (address)ArchiveHeapWriter::NOCOOPS_REQUESTED_BASE; + address requested_base = UseCompressedOops ? ArchiveHeapWriter::narrow_oop_base() : (address)ArchiveHeapWriter::NOCOOPS_REQUESTED_BASE; address requested_start = UseCompressedOops ? ArchiveHeapWriter::buffered_addr_to_requested_addr(buffer_start) : requested_base; - int requested_shift = CompressedOops::shift(); + int requested_shift = ArchiveHeapWriter::narrow_oop_shift(); FakeOop::init_globals(requested_base, requested_start, requested_shift, buffer_start, buffer_end); diff --git a/src/hotspot/share/cds/archiveHeapWriter.cpp b/src/hotspot/share/cds/archiveHeapWriter.cpp index d1a8772874a70..8c4408b6b5f29 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.cpp +++ b/src/hotspot/share/cds/archiveHeapWriter.cpp @@ -53,7 +53,7 @@ GrowableArrayCHeap* ArchiveHeapWriter::_buffer = nullptr; -// The following are offsets from buffer_bottom() +bool ArchiveHeapWriter::_is_writing_deterministic_heap = false; size_t ArchiveHeapWriter::_buffer_used; // Heap root segments @@ -92,6 +92,50 @@ void ArchiveHeapWriter::init() { _source_objs = new GrowableArrayCHeap(10000); guarantee(MIN_GC_REGION_ALIGNMENT <= G1HeapRegion::min_region_size_in_words() * HeapWordSize, "must be"); + + if (CDSConfig::old_cds_flags_used() && !CDSConfig::is_dumping_aot_linked_classes() && UseG1GC) { + // In general, the contents of AOT caches (or CDS archives) are not deterministic: all + // Java programs are multi-threaded (the JDK spawns internal threads), so two training runs + // will collect two profiles that differ due to timing and execution order. + // + // Therefore, we don't try to make the archived heap contents deterministic. There's only + // one exception -- we want JDK builds to be reproducible, so we need to make the + // lib/server/classes*.jsa files deterministic. Here we check for the options + // that are used by make/Images.gmk to generate these files: + // + // * "old" cds flag (-Xshare:dump) is used + // * -XX:+AOTClassLinking is *not* used + // * -XX:+UseG1GC is used. + // + // The logics here must match make/Images.gmk. + _is_writing_deterministic_heap = true; + } + } +} + +// For ArchiveHeapWriter::narrow_oop_{mode, base, shift}(), see comments +// in ArchiveHeapWriter::set_requested_address_range(), +CompressedOops::Mode ArchiveHeapWriter::narrow_oop_mode() { + if (is_writing_deterministic_heap()) { + return CompressedOops::UnscaledNarrowOop; + } else { + return CompressedOops::mode(); + } +} + +address ArchiveHeapWriter::narrow_oop_base() { + if (is_writing_deterministic_heap()) { + return (address)0; + } else { + return CompressedOops::base(); + } +} + +int ArchiveHeapWriter::narrow_oop_shift() { + if (is_writing_deterministic_heap()) { + return 0; + } else { + return CompressedOops::shift(); } } @@ -109,7 +153,7 @@ void ArchiveHeapWriter::write(GrowableArrayCHeap* roots, assert(CDSConfig::is_dumping_heap(), "sanity"); allocate_buffer(); copy_source_objs_to_buffer(roots); - set_requested_address(heap_info); + set_requested_address_range(heap_info); relocate_embedded_oops(roots, heap_info); } @@ -510,7 +554,31 @@ size_t ArchiveHeapWriter::copy_one_source_obj_to_buffer(oop src_obj) { return buffered_obj_offset; } -void ArchiveHeapWriter::set_requested_address(ArchiveHeapInfo* info) { +// Set the range [_requested_bottom, _requested_top), the requested address range of all +// the archived heap objects in the production run. +// +// (1) UseCompressedOops == true && !is_writing_deterministic_heap() +// +// The archived objects are stored using the COOPS encoding of the assembly phase. +// We pick a range within the heap used by the assembly phase. +// +// In the production run, if different COOPS encodings are used: +// - The heap contents needs to be relocated. +// - AOTCodeCache will be disabled. +// +// (2) UseCompressedOops == true && is_writing_deterministic_heap() +// +// We always use zero-based, zero-shift encoding. _requested_top is aligned to 0x10000000. +// +// (3) UseCompressedOops == false: +// +// In the production run, the heap range is usually picked (randomly) by the OS, so we +// will almost always need to perform relocation, regardless of how we pick the requested +// address range. +// +// So we just hard code it to NOCOOPS_REQUESTED_BASE. +// +void ArchiveHeapWriter::set_requested_address_range(ArchiveHeapInfo* info) { assert(!info->is_used(), "only set once"); size_t heap_region_byte_size = _buffer_used; @@ -518,10 +586,25 @@ void ArchiveHeapWriter::set_requested_address(ArchiveHeapInfo* info) { if (UseCompressedOops) { if (UseG1GC) { + // For G1, pick the range at the top of the current heap. If the exact same heap sizes + // are used in the production run, it's likely that we can map the archived objects + // at the requested location to avoid relocation. + size_t alignment = MAX2(G1HeapRegion::GrainBytes, (size_t)MIN_GC_REGION_ALIGNMENT); address heap_end = (address)G1CollectedHeap::heap()->reserved().end(); + if (is_writing_deterministic_heap()) { + // This ensures that all requested addresses can be encoded with zero shifts. Also, + // If the production run uses a small heap (e.g., -Xmx256m), it's likely that + // we can map the archived objects at the requested location to avoid relocation. + heap_end = (address)0x100000000; + } + heap_end = align_up(heap_end, alignment); + log_info(aot, heap)("Heap end = %p", heap_end); - _requested_bottom = align_down(heap_end - heap_region_byte_size, G1HeapRegion::GrainBytes); - _requested_bottom = align_down(_requested_bottom, MIN_GC_REGION_ALIGNMENT); + if (align_up(heap_region_byte_size, alignment) >= (size_t)heap_end) { + log_error(aot, heap)("cached heap space is too large: %zu bytes", heap_region_byte_size); + AOTMetaspace::unrecoverable_writing_error(); + } + _requested_bottom = align_down(heap_end - heap_region_byte_size, alignment); assert(is_aligned(_requested_bottom, G1HeapRegion::GrainBytes), "sanity"); } else { _requested_bottom = align_up(CompressedOops::begin(), MIN_GC_REGION_ALIGNMENT); @@ -598,8 +681,17 @@ template void ArchiveHeapWriter::relocate_field_in_buffer(T* field_ source_referent = HeapShared::scratch_java_mirror(source_referent); assert(source_referent != nullptr, "must be"); } + + // the requested address of the referent oop request_referent = source_obj_to_requested_obj(source_referent); - store_requested_oop_in_buffer(field_addr_in_buffer, request_referent); + if (UseCompressedOops && is_writing_deterministic_heap()) { + // We use zero-based, 0-shift encoding, so the narrowOop is just the lower + // 32 bits of request_referent + intptr_t addr = cast_from_oop(request_referent); + *((narrowOop*)field_addr_in_buffer) = checked_cast(addr); + } else { + store_requested_oop_in_buffer(field_addr_in_buffer, request_referent); + } mark_oop_pointer(field_addr_in_buffer, oopmap); } } diff --git a/src/hotspot/share/cds/archiveHeapWriter.hpp b/src/hotspot/share/cds/archiveHeapWriter.hpp index 80e72c12e7e44..7c0b221cc14cc 100644 --- a/src/hotspot/share/cds/archiveHeapWriter.hpp +++ b/src/hotspot/share/cds/archiveHeapWriter.hpp @@ -28,6 +28,7 @@ #include "cds/heapShared.hpp" #include "memory/allocation.hpp" #include "memory/allStatic.hpp" +#include "oops/compressedOops.hpp" #include "oops/oopHandle.hpp" #include "utilities/bitMap.hpp" #include "utilities/exceptions.hpp" @@ -87,26 +88,11 @@ class ArchiveHeapWriter : AllStatic { // - Each archived object has a "requested address" -- at run time, if the object // can be mapped at this address, we can avoid relocation. // - // The requested address is implemented differently depending on UseCompressedOops: + // The requested address of an archived object is essentially its buffered_addr + delta, + // where delta is (_requested_bottom - buffer_bottom()); // - // UseCompressedOops == true: - // The archived objects are stored assuming that the runtime COOPS compression - // scheme is exactly the same as in dump time (or else a more expensive runtime relocation - // would be needed.) - // - // At dump time, we assume that the runtime heap range is exactly the same as - // in dump time. The requested addresses of the archived objects are chosen such that - // they would occupy the top end of a G1 heap (TBD when dumping is supported by other - // collectors. See JDK-8298614). - // - // UseCompressedOops == false: - // At runtime, the heap range is usually picked (randomly) by the OS, so we will almost always - // need to perform relocation. Hence, the goal of the "requested address" is to ensure that - // the contents of the archived objects are deterministic. I.e., the oop fields of archived - // objects will always point to deterministic addresses. - // - // For G1, the archived heap is written such that the lowest archived object is placed - // at NOCOOPS_REQUESTED_BASE. (TBD after JDK-8298614). + // The requested addresses of all archived objects are within [_requested_bottom, _requested_top). + // See ArchiveHeapWriter::set_requested_address_range() for more info. // ---------------------------------------------------------------------- public: @@ -117,6 +103,15 @@ class ArchiveHeapWriter : AllStatic { // Shenandoah heap region size can never be smaller than 256K. static constexpr int MIN_GC_REGION_ALIGNMENT = 256 * K; + // The heap contents are required to be deterministic when dumping "old" CDS archives, in order + // to support reproducible lib/server/classes*.jsa when building the JDK. + static bool is_writing_deterministic_heap() { return _is_writing_deterministic_heap; } + + // The oop encoding used by the archived heap objects. + static CompressedOops::Mode narrow_oop_mode(); + static address narrow_oop_base(); + static int narrow_oop_shift(); + private: class EmbeddedOopRelocator; struct NativePointerInfo { @@ -124,6 +119,7 @@ class ArchiveHeapWriter : AllStatic { int _field_offset; }; + static bool _is_writing_deterministic_heap; static GrowableArrayCHeap* _buffer; // The number of bytes that have written into _buffer (may be smaller than _buffer->length()). @@ -133,8 +129,8 @@ class ArchiveHeapWriter : AllStatic { static HeapRootSegments _heap_root_segments; // The address range of the requested location of the archived heap objects. - static address _requested_bottom; - static address _requested_top; + static address _requested_bottom; // The requested address of the lowest archived heap object + static address _requested_top; // The exclusive end of the highest archived heap object static GrowableArrayCHeap* _native_pointers; static GrowableArrayCHeap* _source_objs; @@ -202,7 +198,7 @@ class ArchiveHeapWriter : AllStatic { static int filler_array_length(size_t fill_bytes); static HeapWord* init_filler_array_at_buffer_top(int array_length, size_t fill_bytes); - static void set_requested_address(ArchiveHeapInfo* info); + static void set_requested_address_range(ArchiveHeapInfo* info); static void relocate_embedded_oops(GrowableArrayCHeap* roots, ArchiveHeapInfo* info); static void compute_ptrmap(ArchiveHeapInfo *info); static bool is_in_requested_range(oop o); diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 050c1708efb1f..32443620ebc2c 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -217,9 +217,9 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, _compact_strings = CompactStrings; _compact_headers = UseCompactObjectHeaders; if (CDSConfig::is_dumping_heap()) { - _narrow_oop_mode = CompressedOops::mode(); - _narrow_oop_base = CompressedOops::base(); - _narrow_oop_shift = CompressedOops::shift(); + _narrow_oop_mode = ArchiveHeapWriter::narrow_oop_mode(); + _narrow_oop_base = ArchiveHeapWriter::narrow_oop_base(); + _narrow_oop_shift = ArchiveHeapWriter::narrow_oop_shift(); } _compressed_oops = UseCompressedOops; _compressed_class_ptrs = UseCompressedClassPointers; @@ -898,7 +898,7 @@ void FileMapInfo::write_region(int region, char* base, size_t size, assert(!CDSConfig::is_dumping_dynamic_archive(), "must be"); requested_base = (char*)ArchiveHeapWriter::requested_address(); if (UseCompressedOops) { - mapping_offset = (size_t)((address)requested_base - CompressedOops::base()); + mapping_offset = (size_t)((address)requested_base - ArchiveHeapWriter::narrow_oop_base()); assert((mapping_offset >> CompressedOops::shift()) << CompressedOops::shift() == mapping_offset, "must be"); } else { mapping_offset = 0; // not used with !UseCompressedOops diff --git a/test/hotspot/jtreg/ProblemList.txt b/test/hotspot/jtreg/ProblemList.txt index 3dac882116580..4c4f9c53f35b6 100644 --- a/test/hotspot/jtreg/ProblemList.txt +++ b/test/hotspot/jtreg/ProblemList.txt @@ -99,7 +99,6 @@ gc/shenandoah/TestEvilSyncBug.java#generational 8345501 generic-all # :hotspot_runtime -runtime/cds/DeterministicDump.java 8363986 macosx-x64,macosx-aarch64 runtime/jni/terminatedThread/TestTerminatedThread.java 8317789 aix-ppc64 runtime/Monitor/SyncOnValueBasedClassTest.java 8340995 linux-s390x runtime/os/TestTracePageSizes.java#no-options 8267460 linux-aarch64