# HG changeset patch # User iklam # Date 1573691814 28800 # Node ID 22ee476cc6645de19a3cbba068ac30ed72bd6cef # Parent e0d59f0c2b7d0d1d56c6f443edd2070663bd47ff 8231610: Relocate the CDS archive if it cannot be mapped to the requested address Reviewed-by: jiangli, coleenp, ccheung diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/classfile/javaClasses.cpp --- a/src/hotspot/share/classfile/javaClasses.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/classfile/javaClasses.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -1077,7 +1077,7 @@ Klass *ak = (Klass*)(archived_m->metadata_field(_array_klass_offset)); assert(ak != NULL || t == T_VOID, "should not be NULL"); if (ak != NULL) { - Klass *reloc_ak = MetaspaceShared::get_relocated_klass(ak); + Klass *reloc_ak = MetaspaceShared::get_relocated_klass(ak, true); archived_m->metadata_field_put(_array_klass_offset, reloc_ak); } @@ -1222,7 +1222,7 @@ // The archived mirror's field at _klass_offset is still pointing to the original // klass. Updated the field in the archived mirror to point to the relocated // klass in the archive. - Klass *reloc_k = MetaspaceShared::get_relocated_klass(as_Klass(mirror)); + Klass *reloc_k = MetaspaceShared::get_relocated_klass(as_Klass(mirror), true); log_debug(cds, heap, mirror)( "Relocate mirror metadata field at _klass_offset from " PTR_FORMAT " ==> " PTR_FORMAT, p2i(as_Klass(mirror)), p2i(reloc_k)); @@ -1232,7 +1232,7 @@ // higher array klass if exists. Relocate the pointer. Klass *arr = array_klass_acquire(mirror); if (arr != NULL) { - Klass *reloc_arr = MetaspaceShared::get_relocated_klass(arr); + Klass *reloc_arr = MetaspaceShared::get_relocated_klass(arr, true); log_debug(cds, heap, mirror)( "Relocate mirror metadata field at _array_klass_offset from " PTR_FORMAT " ==> " PTR_FORMAT, p2i(arr), p2i(reloc_arr)); @@ -1241,6 +1241,33 @@ return archived_mirror; } +void java_lang_Class::update_archived_primitive_mirror_native_pointers(oop archived_mirror) { + if (MetaspaceShared::relocation_delta() != 0) { + assert(archived_mirror->metadata_field(_klass_offset) == NULL, "must be for primitive class"); + + Klass* ak = ((Klass*)archived_mirror->metadata_field(_array_klass_offset)); + if (ak != NULL) { + archived_mirror->metadata_field_put(_array_klass_offset, + (Klass*)(address(ak) + MetaspaceShared::relocation_delta())); + } + } +} + +void java_lang_Class::update_archived_mirror_native_pointers(oop archived_mirror) { + if (MetaspaceShared::relocation_delta() != 0) { + Klass* k = ((Klass*)archived_mirror->metadata_field(_klass_offset)); + archived_mirror->metadata_field_put(_klass_offset, + (Klass*)(address(k) + MetaspaceShared::relocation_delta())); + + Klass* ak = ((Klass*)archived_mirror->metadata_field(_array_klass_offset)); + if (ak != NULL) { + archived_mirror->metadata_field_put(_array_klass_offset, + (Klass*)(address(ak) + MetaspaceShared::relocation_delta())); + } + } +} + + // Returns true if the mirror is updated, false if no archived mirror // data is present. After the archived mirror object is restored, the // shared klass' _has_raw_archived_mirror flag is cleared. @@ -1256,15 +1283,15 @@ } oop m = HeapShared::materialize_archived_object(k->archived_java_mirror_raw_narrow()); - if (m == NULL) { return false; } + // mirror is archived, restore log_debug(cds, mirror)("Archived mirror is: " PTR_FORMAT, p2i(m)); - - // mirror is archived, restore assert(HeapShared::is_archived_object(m), "must be archived mirror object"); + update_archived_mirror_native_pointers(m); + assert(as_Klass(m) == k, "must be"); Handle mirror(THREAD, m); if (!k->is_array_klass()) { @@ -4649,6 +4676,28 @@ } #endif +#if INCLUDE_CDS_JAVA_HEAP +bool JavaClasses::is_supported_for_archiving(oop obj) { + Klass* klass = obj->klass(); + + if (klass == SystemDictionary::ClassLoader_klass() || // ClassLoader::loader_data is malloc'ed. + klass == SystemDictionary::Module_klass() || // Module::module_entry is malloc'ed + // The next 3 classes are used to implement java.lang.invoke, and are not used directly in + // regular Java code. The implementation of java.lang.invoke uses generated anonymoys classes + // (e.g., as referenced by ResolvedMethodName::vmholder) that are not yet supported by CDS. + // So for now we cannot not support these classes for archiving. + // + // These objects typically are not referenced by static fields, but rather by resolved + // constant pool entries, so excluding them shouldn't affect the archiving of static fields. + klass == SystemDictionary::ResolvedMethodName_klass() || + klass == SystemDictionary::MemberName_klass() || + klass == SystemDictionary::Context_klass()) { + return false; + } + + return true; +} +#endif #ifndef PRODUCT diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/classfile/javaClasses.hpp --- a/src/hotspot/share/classfile/javaClasses.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/classfile/javaClasses.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -281,6 +281,8 @@ Handle protection_domain, TRAPS); static void fixup_mirror(Klass* k, TRAPS); static oop create_basic_type_mirror(const char* basic_type_name, BasicType type, TRAPS); + static void update_archived_primitive_mirror_native_pointers(oop archived_mirror) NOT_CDS_JAVA_HEAP_RETURN; + static void update_archived_mirror_native_pointers(oop archived_mirror) NOT_CDS_JAVA_HEAP_RETURN; // Archiving static void serialize_offsets(SerializeClosure* f) NOT_CDS_RETURN; @@ -1662,6 +1664,7 @@ static void check_offsets() PRODUCT_RETURN; static void serialize_offsets(SerializeClosure* soc) NOT_CDS_RETURN; static InjectedField* get_injected(Symbol* class_name, int* field_count); + static bool is_supported_for_archiving(oop obj) NOT_CDS_JAVA_HEAP_RETURN_(false); }; #undef DECLARE_INJECTED_FIELD_ENUM diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/classfile/systemDictionaryShared.cpp --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -38,6 +38,7 @@ #include "classfile/vmSymbols.hpp" #include "logging/log.hpp" #include "memory/allocation.hpp" +#include "memory/archiveUtils.hpp" #include "memory/filemap.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspaceClosure.hpp" @@ -294,6 +295,7 @@ if (DynamicDumpSharedSpaces) { _klass = DynamicArchive::original_to_target(info._klass); } + ArchivePtrMarker::mark_pointer(&_klass); } bool matches(int clsfile_size, int clsfile_crc32) const { @@ -337,6 +339,8 @@ } else { *info_pointer_addr(klass) = record; } + + ArchivePtrMarker::mark_pointer(info_pointer_addr(klass)); } // Used by RunTimeSharedDictionary to implement OffsetCompactHashtable::EQUALS @@ -1354,7 +1358,7 @@ if (DynamicDumpSharedSpaces) { name = DynamicArchive::original_to_target(name); } - hash = primitive_hash(name); + hash = SystemDictionaryShared::hash_for_shared_dictionary(name); u4 delta; if (DynamicDumpSharedSpaces) { delta = MetaspaceShared::object_delta_u4(DynamicArchive::buffer_to_target(record)); @@ -1413,7 +1417,7 @@ return NULL; } - unsigned int hash = primitive_hash(name); + unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary(name); const RunTimeSharedClassInfo* record = NULL; if (!MetaspaceShared::is_shared_dynamic(name)) { // The names of all shared classes in the static dict must also be in the diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/classfile/systemDictionaryShared.hpp --- a/src/hotspot/share/classfile/systemDictionaryShared.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/classfile/systemDictionaryShared.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -323,6 +323,12 @@ }; #endif + template + static unsigned int hash_for_shared_dictionary(T* ptr) { + assert(ptr > (T*)SharedBaseAddress, "must be"); + address p = address(ptr) - SharedBaseAddress; + return primitive_hash
(p); + } }; #endif // SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/include/cds.h --- a/src/hotspot/share/include/cds.h Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/include/cds.h Wed Nov 13 16:36:54 2019 -0800 @@ -33,26 +33,29 @@ // // Also, this is a C header file. Do not use C++ here. -#define NUM_CDS_REGIONS 8 // this must be the same as MetaspaceShared::n_regions +#define NUM_CDS_REGIONS 9 // this must be the same as MetaspaceShared::n_regions #define CDS_ARCHIVE_MAGIC 0xf00baba2 #define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8 -#define CURRENT_CDS_ARCHIVE_VERSION 8 +#define CURRENT_CDS_ARCHIVE_VERSION 9 #define INVALID_CDS_ARCHIVE_VERSION -1 struct CDSFileMapRegion { - int _crc; // crc checksum of the current space - size_t _file_offset; // sizeof(this) rounded to vm page size - union { - char* _base; // copy-on-write base address - size_t _offset; // offset from the compressed oop encoding base, only used - // by archive heap space - } _addr; - size_t _used; // for setting space top on read - int _read_only; // read only space? - int _allow_exec; // executable code in space? - void* _oopmap; // bitmap for relocating embedded oops - size_t _oopmap_size_in_bits; - int _is_heap_region; // used in debug build only. + int _crc; // CRC checksum of this region. + int _read_only; // read only region? + int _allow_exec; // executable code in this region? + int _is_heap_region; // Used by SA and debug build. + int _is_bitmap_region; // Relocation bitmap for RO/RW/MC/MD regions (used by SA and debug build). + int _mapped_from_file; // Is this region mapped from a file? + // If false, this region was initialized using os::read(). + size_t _file_offset; // Data for this region starts at this offset in the archive file. + size_t _mapping_offset; // This region should be mapped at this offset from the base address + // - for non-heap regions, the base address is SharedBaseAddress + // - for heap regions, the base address is the compressed oop encoding base + size_t _used; // Number of bytes actually used by this region (excluding padding bytes added + // for alignment purposed. + size_t _oopmap_offset; // Bitmap for relocating embedded oops (offset from SharedBaseAddress). + size_t _oopmap_size_in_bits; + char* _mapped_base; // Actually mapped address (NULL if this region is not mapped). }; struct CDSFileMapHeaderBase { diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/allocation.hpp --- a/src/hotspot/share/memory/allocation.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/allocation.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -283,11 +283,6 @@ _shared_metaspace_top = top; } - static void expand_shared_metaspace_range(void* top) { - assert(top >= _shared_metaspace_top, "must be"); - _shared_metaspace_top = top; - } - static void* shared_metaspace_base() { return _shared_metaspace_base; } static void* shared_metaspace_top() { return _shared_metaspace_top; } diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/archiveUtils.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/memory/archiveUtils.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019, 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. + * + */ + +#include "precompiled.hpp" +#include "memory/archiveUtils.hpp" +#include "memory/metaspace.hpp" +#include "utilities/bitMap.inline.hpp" + +#if INCLUDE_CDS + +CHeapBitMap* ArchivePtrMarker::_ptrmap = NULL; +address* ArchivePtrMarker::_ptr_base; +address* ArchivePtrMarker::_ptr_end; +bool ArchivePtrMarker::_compacted; + +void ArchivePtrMarker::initialize(CHeapBitMap* ptrmap, address* ptr_base, address* ptr_end) { + assert(_ptrmap == NULL, "initialize only once"); + _ptr_base = ptr_base; + _ptr_end = ptr_end; + _compacted = false; + _ptrmap = ptrmap; + + // Use this as initial guesstimate. We should need less space in the + // archive, but if we're wrong the bitmap will be expanded automatically. + size_t estimated_archive_size = MetaspaceGC::capacity_until_GC(); + // But set it smaller in debug builds so we always test the expansion code. + // (Default archive is about 12MB). + DEBUG_ONLY(estimated_archive_size = 6 * M); + + // We need one bit per pointer in the archive. + _ptrmap->initialize(estimated_archive_size / sizeof(intptr_t)); +} + +void ArchivePtrMarker::mark_pointer(address* ptr_loc) { + assert(_ptrmap != NULL, "not initialized"); + assert(!_compacted, "cannot mark anymore"); + + if (_ptr_base <= ptr_loc && ptr_loc < _ptr_end) { + address value = *ptr_loc; + if (value != NULL) { + assert(uintx(ptr_loc) % sizeof(intptr_t) == 0, "pointers must be stored in aligned addresses"); + size_t idx = ptr_loc - _ptr_base; + if (_ptrmap->size() <= idx) { + _ptrmap->resize((idx + 1) * 2); + } + assert(idx < _ptrmap->size(), "must be"); + _ptrmap->set_bit(idx); + //tty->print_cr("Marking pointer [%p] -> %p @ " SIZE_FORMAT_W(9), ptr_loc, *ptr_loc, idx); + } + } +} + +class ArchivePtrBitmapCleaner: public BitMapClosure { + CHeapBitMap* _ptrmap; + address* _ptr_base; + address _relocatable_base; + address _relocatable_end; + size_t _max_non_null_offset; + +public: + ArchivePtrBitmapCleaner(CHeapBitMap* ptrmap, address* ptr_base, address relocatable_base, address relocatable_end) : + _ptrmap(ptrmap), _ptr_base(ptr_base), + _relocatable_base(relocatable_base), _relocatable_end(relocatable_end), _max_non_null_offset(0) {} + + bool do_bit(size_t offset) { + address* ptr_loc = _ptr_base + offset; + address ptr_value = *ptr_loc; + if (ptr_value != NULL) { + assert(_relocatable_base <= ptr_value && ptr_value < _relocatable_end, "do not point to arbitrary locations!"); + if (_max_non_null_offset < offset) { + _max_non_null_offset = offset; + } + } else { + _ptrmap->clear_bit(offset); + DEBUG_ONLY(log_trace(cds, reloc)("Clearing pointer [" PTR_FORMAT "] -> NULL @ " SIZE_FORMAT_W(9), p2i(ptr_loc), offset)); + } + + return true; + } + + size_t max_non_null_offset() const { return _max_non_null_offset; } +}; + +void ArchivePtrMarker::compact(address relocatable_base, address relocatable_end) { + assert(!_compacted, "cannot compact again"); + ArchivePtrBitmapCleaner cleaner(_ptrmap, _ptr_base, relocatable_base, relocatable_end); + _ptrmap->iterate(&cleaner); + compact(cleaner.max_non_null_offset()); +} + +void ArchivePtrMarker::compact(size_t max_non_null_offset) { + assert(!_compacted, "cannot compact again"); + _ptrmap->resize(max_non_null_offset + 1); + _compacted = true; +} + +#endif // INCLUDE_CDS diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/archiveUtils.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/hotspot/share/memory/archiveUtils.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -0,0 +1,136 @@ +/* + * Copyright (c) 2019, 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. + * + */ + +#ifndef SHARE_MEMORY_ARCHIVEUTILS_HPP +#define SHARE_MEMORY_ARCHIVEUTILS_HPP + +#include "logging/log.hpp" +#include "runtime/arguments.hpp" +#include "utilities/bitMap.hpp" + +// ArchivePtrMarker is used to mark the location of pointers embedded in a CDS archive. E.g., when an +// InstanceKlass k is dumped, we mark the location of the k->_name pointer by effectively calling +// mark_pointer(/*ptr_loc=*/&k->_name). It's required that (_prt_base <= ptr_loc < _ptr_end). _ptr_base is +// fixed, but _ptr_end can be expanded as more objects are dumped. +class ArchivePtrMarker : AllStatic { + static CHeapBitMap* _ptrmap; + static address* _ptr_base; + static address* _ptr_end; + + // Once _ptrmap is compacted, we don't allow bit marking anymore. This is to + // avoid unintentional copy operations after the bitmap has been finalized and written. + static bool _compacted; +public: + static void initialize(CHeapBitMap* ptrmap, address* ptr_base, address* ptr_end); + static void mark_pointer(address* ptr_loc); + static void compact(address relocatable_base, address relocatable_end); + static void compact(size_t max_non_null_offset); + + template + static void mark_pointer(T* ptr_loc) { + mark_pointer((address*)ptr_loc); + } + + static void expand_ptr_end(address *new_ptr_end) { + assert(_ptr_end <= new_ptr_end, "must be"); + _ptr_end = new_ptr_end; + } + + static CHeapBitMap* ptrmap() { + return _ptrmap; + } +}; + +// SharedDataRelocator is used to shift pointers in the CDS archive. +// +// The CDS archive is basically a contiguous block of memory (divided into several regions) +// that contains multiple objects. The objects may contain direct pointers that point to other objects +// within the archive (e.g., InstanceKlass::_name points to a Symbol in the archive). During dumping, we +// built a bitmap that marks the locations of all these pointers (using ArchivePtrMarker, see comments above). +// +// The contents of the archive assumes that it’s mapped at the default SharedBaseAddress (e.g. 0x800000000). +// If the archive ends up being mapped at a different address (e.g. 0x810000000), SharedDataRelocator +// is used to shift each marked pointer by a delta (0x10000000 in this example), so that it points to +// the actually mapped location of the target object. +template +class SharedDataRelocator: public BitMapClosure { + // for all (address** p), where (is_marked(p) && _patch_base <= p && p < _patch_end) { *p += delta; } + + // Patch all pointers within this region that are marked. + address* _patch_base; + address* _patch_end; + + // Before patching, all pointers must point to this region. + address _valid_old_base; + address _valid_old_end; + + // After patching, all pointers must point to this region. + address _valid_new_base; + address _valid_new_end; + + // How much to relocate for each pointer. + intx _delta; + + // The following fields are used only when COMPACTING == true; + // The highest offset (inclusive) in the bitmap that contains a non-null pointer. + // This is used at dump time to reduce the size of the bitmap (which may have been over-allocated). + size_t _max_non_null_offset; + CHeapBitMap* _ptrmap; + + public: + SharedDataRelocator(address* patch_base, address* patch_end, + address valid_old_base, address valid_old_end, + address valid_new_base, address valid_new_end, intx delta, + CHeapBitMap* ptrmap = NULL) : + _patch_base(patch_base), _patch_end(patch_end), + _valid_old_base(valid_old_base), _valid_old_end(valid_old_end), + _valid_new_base(valid_new_base), _valid_new_end(valid_new_end), + _delta(delta) { + log_debug(cds, reloc)("SharedDataRelocator::_patch_base = " PTR_FORMAT, p2i(_patch_base)); + log_debug(cds, reloc)("SharedDataRelocator::_patch_end = " PTR_FORMAT, p2i(_patch_end)); + log_debug(cds, reloc)("SharedDataRelocator::_valid_old_base = " PTR_FORMAT, p2i(_valid_old_base)); + log_debug(cds, reloc)("SharedDataRelocator::_valid_old_end = " PTR_FORMAT, p2i(_valid_old_end)); + log_debug(cds, reloc)("SharedDataRelocator::_valid_new_base = " PTR_FORMAT, p2i(_valid_new_base)); + log_debug(cds, reloc)("SharedDataRelocator::_valid_new_end = " PTR_FORMAT, p2i(_valid_new_end)); + if (COMPACTING) { + assert(ptrmap != NULL, "must be"); + _max_non_null_offset = 0; + _ptrmap = ptrmap; + } else { + // Don't touch the _max_non_null_offset and _ptrmap fields. Hopefully a good C++ compiler can + // elide them. + assert(ptrmap == NULL, "must be"); + } + } + + size_t max_non_null_offset() { + assert(COMPACTING, "must be"); + return _max_non_null_offset; + } + + inline bool do_bit(size_t offset); +}; + + +#endif // SHARE_MEMORY_ARCHIVEUTILS_HPP diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/dynamicArchive.cpp --- a/src/hotspot/share/memory/dynamicArchive.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/dynamicArchive.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -29,12 +29,13 @@ #include "classfile/systemDictionary.hpp" #include "classfile/systemDictionaryShared.hpp" #include "logging/log.hpp" +#include "memory/archiveUtils.inline.hpp" +#include "memory/dynamicArchive.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspace.hpp" #include "memory/metaspaceClosure.hpp" #include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" -#include "memory/dynamicArchive.hpp" #include "oops/compressedOops.hpp" #include "oops/objArrayKlass.hpp" #include "prims/jvmtiRedefineClasses.hpp" @@ -50,7 +51,6 @@ #endif class DynamicArchiveBuilder : ResourceObj { - CHeapBitMap _ptrmap; static unsigned my_hash(const address& a) { return primitive_hash
(a); } @@ -64,7 +64,7 @@ 16384, ResourceObj::C_HEAP> RelocationTable; RelocationTable _new_loc_table; - intx _buffer_to_target_delta; + static intx _buffer_to_target_delta; DumpRegion* _current_dump_space; @@ -77,10 +77,7 @@ public: void mark_pointer(address* ptr_loc) { - if (is_in_buffer_space(ptr_loc)) { - size_t idx = pointer_delta(ptr_loc, _alloc_bottom, sizeof(address)); - _ptrmap.set_bit(idx); - } + ArchivePtrMarker::mark_pointer(ptr_loc); } DumpRegion* current_dump_space() const { @@ -128,6 +125,28 @@ return pp != NULL; } + static int dynamic_dump_method_comparator(Method* a, Method* b) { + Symbol* a_name = a->name(); + Symbol* b_name = b->name(); + + if (a_name == b_name) { + return 0; + } + + if (!MetaspaceShared::is_in_shared_metaspace(a_name)) { + // a_name points to a Symbol in the top archive. + // When this method is called, a_name is still pointing to the output space. + // Translate it to point to the output space, so that it can be compared with + // Symbols in the base archive. + a_name = (Symbol*)(address(a_name) + _buffer_to_target_delta); + } + if (!MetaspaceShared::is_in_shared_metaspace(b_name)) { + b_name = (Symbol*)(address(b_name) + _buffer_to_target_delta); + } + + return a_name->fast_compare(b_name); + } + protected: enum FollowMode { make_a_copy, point_to_it, set_to_null @@ -240,6 +259,16 @@ return true; // keep recursing until every object is visited exactly once. } + + virtual void push_special(SpecialRef type, Ref* ref, intptr_t* p) { + assert(type == _method_entry_ref, "only special type allowed for now"); + address obj = ref->obj(); + address new_obj = _builder->get_new_loc(ref); + size_t offset = pointer_delta(p, obj, sizeof(u1)); + intptr_t* new_p = (intptr_t*)(new_obj + offset); + assert(*p == *new_p, "must be a copy"); + ArchivePtrMarker::mark_pointer((address*)new_p); + } }; class EmbeddedRefUpdater: public MetaspaceClosure { @@ -331,7 +360,7 @@ public: EmbeddedRefMarker(DynamicArchiveBuilder* shuffler) : _builder(shuffler) {} virtual bool do_ref(Ref* ref, bool read_only) { - if (ref->not_null() && _builder->is_in_buffer_space(ref->obj())) { + if (ref->not_null()) { _builder->mark_pointer(ref->addr()); } return false; // Do not recurse. @@ -441,10 +470,10 @@ p2i(obj), p2i(p), bytes, MetaspaceObj::type_name(ref->msotype())); memcpy(p, obj, bytes); - intptr_t* cloned_vtable = MetaspaceShared::fix_cpp_vtable_for_dynamic_archive(ref->msotype(), p); if (cloned_vtable != NULL) { update_pointer((address*)p, (address)cloned_vtable, "vtb", 0, /*is_mso_pointer*/false); + mark_pointer((address*)p); } return (address)p; @@ -551,6 +580,9 @@ address reserved_bottom = reserve_space_and_init_buffer_to_target_delta(); init_header(reserved_bottom); + CHeapBitMap ptrmap; + ArchivePtrMarker::initialize(&ptrmap, (address*)reserved_bottom, (address*)current_dump_space()->top()); + verify_estimate_size(sizeof(DynamicArchiveHeader), "header"); log_info(cds, dynamic)("Copying %d klasses and %d symbols", @@ -576,10 +608,6 @@ iterate_roots(&ro_copier); } - size_t bitmap_size = pointer_delta(current_dump_space()->top(), - _alloc_bottom, sizeof(address)); - _ptrmap.initialize(bitmap_size); - { log_info(cds)("Relocating embedded pointers ... "); ResourceMark rm; @@ -653,7 +681,7 @@ it->push(&_symbols->at(i)); } - _header->shared_path_table_metaspace_pointers_do(it); + FileMapInfo::metaspace_pointers_do(it); // Do not call these again, as we have already collected all the classes and symbols // that we want to archive. Also, these calls would corrupt the tables when @@ -666,6 +694,9 @@ } }; +intx DynamicArchiveBuilder::_buffer_to_target_delta; + + size_t DynamicArchiveBuilder::estimate_archive_size() { // size of the symbol table and two dictionaries, plus the RunTimeSharedClassInfo's _estimated_hashtable_bytes = 0; @@ -688,26 +719,16 @@ address DynamicArchiveBuilder::reserve_space_and_init_buffer_to_target_delta() { size_t total = estimate_archive_size(); - bool large_pages = false; // No large pages when dumping the CDS archive. - size_t increment = align_up(1*G, reserve_alignment()); - char* addr = (char*)align_up(CompressedKlassPointers::base() + MetaspaceSize + increment, - reserve_alignment()); - - ReservedSpace* rs = MetaspaceShared::reserve_shared_rs( - total, reserve_alignment(), large_pages, addr); - while (!rs->is_reserved() && (addr + increment > addr)) { - addr += increment; - rs = MetaspaceShared::reserve_shared_rs( - total, reserve_alignment(), large_pages, addr); - } - if (!rs->is_reserved()) { + ReservedSpace rs = MetaspaceShared::reserve_shared_space(total); + if (!rs.is_reserved()) { log_error(cds, dynamic)("Failed to reserve %d bytes of output buffer.", (int)total); vm_direct_exit(0); } - address buffer_base = (address)rs->base(); + address buffer_base = (address)rs.base(); log_info(cds, dynamic)("Reserved output buffer space at : " PTR_FORMAT " [%d bytes]", p2i(buffer_base), (int)total); + MetaspaceShared::set_shared_rs(rs); // At run time, we will mmap the dynamic archive at target_space_bottom. // However, at dump time, we may not be able to write into the target_space, @@ -788,6 +809,7 @@ void DynamicArchiveBuilder::make_klasses_shareable() { int i, count = _klasses->length(); + InstanceKlass::disable_method_binary_search(); for (i = 0; i < count; i++) { InstanceKlass* ik = _klasses->at(i); sort_methods(ik); @@ -847,18 +869,24 @@ } #ifdef ASSERT - { + if (ik->methods() != NULL) { for (int m = 0; m < ik->methods()->length(); m++) { Symbol* name = ik->methods()->at(m)->name(); assert(MetaspaceShared::is_in_shared_metaspace(name) || is_in_buffer_space(name), "must be"); } } + if (ik->default_methods() != NULL) { + for (int m = 0; m < ik->default_methods()->length(); m++) { + Symbol* name = ik->default_methods()->at(m)->name(); + assert(MetaspaceShared::is_in_shared_metaspace(name) || is_in_buffer_space(name), "must be"); + } + } #endif Thread* THREAD = Thread::current(); - Method::sort_methods(ik->methods()); + Method::sort_methods(ik->methods(), /*set_idnums=*/true, dynamic_dump_method_comparator); if (ik->default_methods() != NULL) { - Method::sort_methods(ik->default_methods(), /*set_idnums=*/false); + Method::sort_methods(ik->default_methods(), /*set_idnums=*/false, dynamic_dump_method_comparator); } ik->vtable().initialize_vtable(true, THREAD); assert(!HAS_PENDING_EXCEPTION, "cannot fail"); ik->itable().initialize_itable(true, THREAD); assert(!HAS_PENDING_EXCEPTION, "cannot fail"); @@ -902,14 +930,60 @@ } }; - void DynamicArchiveBuilder::relocate_buffer_to_target() { RelocateBufferToTarget patcher(this, (address*)_alloc_bottom, _buffer_to_target_delta); - _ptrmap.iterate(&patcher); + ArchivePtrMarker::ptrmap()->iterate(&patcher); + + Array* table = FileMapInfo::shared_path_table().table(); + SharedPathTable runtime_table(to_target(table), FileMapInfo::shared_path_table().size()); + _header->set_shared_path_table(runtime_table); + + address relocatable_base = (address)SharedBaseAddress; + address relocatable_end = (address)(current_dump_space()->top()) + _buffer_to_target_delta; + + intx addr_delta = MetaspaceShared::final_delta(); + if (addr_delta == 0) { + ArchivePtrMarker::compact(relocatable_base, relocatable_end); + } else { + // The base archive is NOT mapped at Arguments::default_SharedBaseAddress() (due to ASLR). + // This means that the current content of the dynamic archive is based on a random + // address. Let's relocate all the pointers, so that it can be mapped to + // Arguments::default_SharedBaseAddress() without runtime relocation. + // + // Note: both the base and dynamic archive are written with + // FileMapHeader::_shared_base_address == Arguments::default_SharedBaseAddress() + + // Patch all pointers that are marked by ptrmap within this region, + // where we have just dumped all the metaspace data. + address patch_base = (address)_alloc_bottom; + address patch_end = (address)current_dump_space()->top(); - Array* table = _header->shared_path_table().table(); - table = to_target(table); - _header->relocate_shared_path_table(table); + // the current value of the pointers to be patched must be within this + // range (i.e., must point to either the top archive (as currently mapped), or to the + // (targeted address of) the top archive) + address valid_old_base = relocatable_base; + address valid_old_end = relocatable_end; + size_t base_plus_top_size = valid_old_end - valid_old_base; + size_t top_size = patch_end - patch_base; + size_t base_size = base_plus_top_size - top_size; + assert(base_plus_top_size > base_size, "no overflow"); + assert(base_plus_top_size > top_size, "no overflow"); + + // after patching, the pointers must point inside this range + // (the requested location of the archive, as mapped at runtime). + address valid_new_base = (address)Arguments::default_SharedBaseAddress(); + address valid_new_end = valid_new_base + base_plus_top_size; + + log_debug(cds)("Relocating archive from [" INTPTR_FORMAT " - " INTPTR_FORMAT "] to " + "[" INTPTR_FORMAT " - " INTPTR_FORMAT "], delta = " INTX_FORMAT " bytes", + p2i(patch_base + base_size), p2i(patch_end), + p2i(valid_new_base + base_size), p2i(valid_new_end), addr_delta); + + SharedDataRelocator patcher((address*)patch_base, (address*)patch_end, valid_old_base, valid_old_end, + valid_new_base, valid_new_end, addr_delta, ArchivePtrMarker::ptrmap()); + ArchivePtrMarker::ptrmap()->iterate(&patcher); + ArchivePtrMarker::compact(patcher.max_non_null_offset()); + } } void DynamicArchiveBuilder::write_regions(FileMapInfo* dynamic_info) { @@ -925,6 +999,7 @@ MetaspaceShared::misc_code_dump_space()->base(), MetaspaceShared::misc_code_dump_space()->used(), /*read_only=*/false,/*allow_exec=*/true); + dynamic_info->write_bitmap_region(ArchivePtrMarker::ptrmap()); } void DynamicArchiveBuilder::write_archive(char* serialized_data_start) { @@ -940,6 +1015,7 @@ const char* archive_name = Arguments::GetSharedDynamicArchivePath(); dynamic_info->open_for_write(archive_name); write_regions(dynamic_info); + dynamic_info->set_final_requested_base((char*)Arguments::default_SharedBaseAddress()); dynamic_info->set_header_crc(dynamic_info->compute_header_crc()); dynamic_info->write_header(); dynamic_info->close(); @@ -948,6 +1024,8 @@ address top = address(current_dump_space()->top()) + _buffer_to_target_delta; size_t file_size = pointer_delta(top, base, sizeof(char)); + base += MetaspaceShared::final_delta(); + top += MetaspaceShared::final_delta(); log_info(cds, dynamic)("Written dynamic archive " PTR_FORMAT " - " PTR_FORMAT " [" SIZE_FORMAT " bytes header, " SIZE_FORMAT " bytes total]", p2i(base), p2i(top), _header->header_size(), file_size); @@ -1036,79 +1114,8 @@ } -static DynamicArchiveHeader *_dynamic_header = NULL; DynamicArchiveBuilder* DynamicArchive::_builder = NULL; -void DynamicArchive::map_failed(FileMapInfo* mapinfo) { - if (mapinfo->dynamic_header() != NULL) { - os::free((void*)mapinfo->dynamic_header()); - } - delete mapinfo; -} - -// Returns the top of the mapped address space -address DynamicArchive::map() { - assert(UseSharedSpaces, "Sanity"); - - // Create the dynamic archive map info - FileMapInfo* mapinfo; - const char* filename = Arguments::GetSharedDynamicArchivePath(); - struct stat st; - address result; - if ((filename != NULL) && (os::stat(filename, &st) == 0)) { - mapinfo = new FileMapInfo(false); - if (!mapinfo->open_for_read(filename)) { - result = NULL; - } - result = map_impl(mapinfo); - if (result == NULL) { - map_failed(mapinfo); - mapinfo->restore_shared_path_table(); - } - } else { - if (filename != NULL) { - log_warning(cds, dynamic)("specified dynamic archive doesn't exist: %s", filename); - } - result = NULL; - } - return result; -} - -address DynamicArchive::map_impl(FileMapInfo* mapinfo) { - // Read header - if (!mapinfo->initialize(false)) { - return NULL; - } - - _dynamic_header = mapinfo->dynamic_header(); - int regions[] = {MetaspaceShared::rw, - MetaspaceShared::ro, - MetaspaceShared::mc}; - - size_t len = sizeof(regions)/sizeof(int); - char* saved_base[] = {NULL, NULL, NULL}; - char* top = mapinfo->map_regions(regions, saved_base, len); - if (top == NULL) { - mapinfo->unmap_regions(regions, saved_base, len); - FileMapInfo::fail_continue("Unable to use dynamic archive. Failed map_region for using -Xshare:on."); - return NULL; - } - - if (!validate(mapinfo)) { - return NULL; - } - - if (_dynamic_header == NULL) { - return NULL; - } - - intptr_t* buffer = (intptr_t*)_dynamic_header->serialized_data_start(); - ReadClosure rc(&buffer); - SymbolTable::serialize_shared_table_header(&rc, false); - SystemDictionaryShared::serialize_dictionary_headers(&rc, false); - - return (address)top; -} bool DynamicArchive::validate(FileMapInfo* dynamic_info) { // Check if the recorded base archive matches with the current one @@ -1136,11 +1143,3 @@ } return true; } - -bool DynamicArchive::is_mapped() { - return (_dynamic_header != NULL); -} - -void DynamicArchive::disable() { - _dynamic_header = NULL; -} diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/dynamicArchive.hpp --- a/src/hotspot/share/memory/dynamicArchive.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/dynamicArchive.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -99,13 +99,8 @@ // archive? static bool is_in_target_space(void *obj); - static address map(); - static bool is_mapped(); + static bool is_mapped() { return FileMapInfo::dynamic_info() != NULL; } static bool validate(FileMapInfo* dynamic_info); - static void disable(); -private: - static address map_impl(FileMapInfo* mapinfo); - static void map_failed(FileMapInfo* mapinfo); }; #endif // INCLUDE_CDS #endif // SHARE_VM_MEMORY_DYNAMICARCHIVE_HPP diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/filemap.cpp --- a/src/hotspot/share/memory/filemap.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/filemap.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -34,6 +34,7 @@ #include "logging/log.hpp" #include "logging/logStream.hpp" #include "logging/logMessage.hpp" +#include "memory/archiveUtils.inline.hpp" #include "memory/dynamicArchive.hpp" #include "memory/filemap.hpp" #include "memory/heapShared.inline.hpp" @@ -55,6 +56,7 @@ #include "runtime/vm_version.hpp" #include "services/memTracker.hpp" #include "utilities/align.hpp" +#include "utilities/bitMap.inline.hpp" #include "utilities/classpathStream.hpp" #include "utilities/defaultStream.hpp" #if INCLUDE_G1GC @@ -69,9 +71,6 @@ #define O_BINARY 0 // otherwise do nothing. #endif -extern address JVM_FunctionAtStart(); -extern address JVM_FunctionAtEnd(); - // Complain and stop. All error conditions occurring during the writing of // an archive file should stop the process. Unrecoverable errors during // the reading of the archive file should stop the process. @@ -104,12 +103,6 @@ void FileMapInfo::fail_continue(const char *msg, ...) { va_list ap; va_start(ap, msg); - if (_dynamic_archive_info == NULL) { - MetaspaceShared::set_archive_loading_failed(); - } else { - // _dynamic_archive_info has been setup after mapping the base archive - DynamicArchive::disable(); - } if (PrintSharedArchiveAndExit && _validating_shared_path_table) { // If we are doing PrintSharedArchiveAndExit and some of the classpath entries // do not validate, we can still continue "limping" to validate the remaining @@ -128,15 +121,6 @@ ls.vprint_cr(msg, ap); } } - if (_dynamic_archive_info == NULL) { - UseSharedSpaces = false; - assert(current_info() != NULL, "singleton must be registered"); - current_info()->close(); - } else { - // We are failing when loading the top archive, but the base archive should - // continue to work. - log_warning(cds, dynamic)("Unable to use shared archive. The top archive failed to load: %s", _dynamic_archive_info->_full_path); - } } va_end(ap); } @@ -227,9 +211,7 @@ _narrow_oop_base = CompressedOops::base(); _narrow_oop_shift = CompressedOops::shift(); _max_heap_size = MaxHeapSize; - _narrow_klass_base = CompressedKlassPointers::base(); _narrow_klass_shift = CompressedKlassPointers::shift(); - _shared_path_table = mapinfo->_shared_path_table; if (HeapShared::is_heap_object_archiving_allowed()) { _heap_end = CompressedOops::end(); } @@ -249,11 +231,16 @@ _verify_local = BytecodeVerificationLocal; _verify_remote = BytecodeVerificationRemote; _has_platform_or_app_classes = ClassLoaderExt::has_platform_or_app_classes(); - _shared_base_address = SharedBaseAddress; + _requested_base_address = (char*)SharedBaseAddress; + _mapped_base_address = (char*)SharedBaseAddress; _allow_archiving_with_java_agent = AllowArchivingWithJavaAgent; // the following 2 fields will be set in write_header for dynamic archive header _base_archive_name_size = 0; _base_archive_is_default = false; + + if (!DynamicDumpSharedSpaces) { + set_shared_path_table(mapinfo->_shared_path_table); + } } void SharedClassPathEntry::init_as_non_existent(const char* path, TRAPS) { @@ -615,9 +602,11 @@ return path_array; } -bool FileMapInfo::fail(const char* msg, const char* name) { +bool FileMapInfo::classpath_failure(const char* msg, const char* name) { ClassLoader::trace_class_path(msg, name); - MetaspaceShared::set_archive_loading_failed(); + if (PrintSharedArchiveAndExit) { + MetaspaceShared::set_archive_loading_failed(); + } return false; } @@ -692,7 +681,7 @@ if (mismatch) { // The paths are different - return fail("[BOOT classpath mismatch, actual =", runtime_boot_path); + return classpath_failure("[BOOT classpath mismatch, actual =", runtime_boot_path); } return true; } @@ -703,7 +692,7 @@ int rp_len = num_paths(appcp); bool mismatch = false; if (rp_len < shared_app_paths_len) { - return fail("Run time APP classpath is shorter than the one at dump time: ", appcp); + return classpath_failure("Run time APP classpath is shorter than the one at dump time: ", appcp); } if (shared_app_paths_len != 0 && rp_len != 0) { // Prefix is OK: E.g., dump with -cp foo.jar, but run with -cp foo.jar:bar.jar. @@ -711,7 +700,7 @@ GrowableArray* rp_array = create_path_array(appcp); if (rp_array->length() == 0) { // None of the jar file specified in the runtime -cp exists. - return fail("None of the jar file specified in the runtime -cp exists: -Djava.class.path=", appcp); + return classpath_failure("None of the jar file specified in the runtime -cp exists: -Djava.class.path=", appcp); } // Handling of non-existent entries in the classpath: we eliminate all the non-existent @@ -726,7 +715,7 @@ int j = header()->app_class_paths_start_index(); mismatch = check_paths(j, shared_app_paths_len, rp_array); if (mismatch) { - return fail("[APP classpath mismatch, actual: -Djava.class.path=", appcp); + return classpath_failure("[APP classpath mismatch, actual: -Djava.class.path=", appcp); } } return true; @@ -952,8 +941,8 @@ // Read the FileMapInfo information from the file. -bool FileMapInfo::init_from_file(int fd, bool is_static) { - size_t sz = is_static ? sizeof(FileMapHeader) : sizeof(DynamicArchiveHeader); +bool FileMapInfo::init_from_file(int fd) { + size_t sz = is_static() ? sizeof(FileMapHeader) : sizeof(DynamicArchiveHeader); size_t n = os::read(fd, header(), (unsigned int)sz); if (n != sz) { fail_continue("Unable to read the file header."); @@ -965,7 +954,7 @@ return false; } - unsigned int expected_magic = is_static ? CDS_ARCHIVE_MAGIC : CDS_DYNAMIC_ARCHIVE_MAGIC; + unsigned int expected_magic = is_static() ? CDS_ARCHIVE_MAGIC : CDS_DYNAMIC_ARCHIVE_MAGIC; if (header()->magic() != expected_magic) { log_info(cds)("_magic expected: 0x%08x", expected_magic); log_info(cds)(" actual: 0x%08x", header()->magic()); @@ -1016,7 +1005,7 @@ _file_offset = n + header()->base_archive_name_size(); // accounts for the size of _base_archive_name - if (is_static) { + if (is_static()) { // just checking the last region is sufficient since the archive is written // in sequential order size_t len = lseek(fd, 0, SEEK_END); @@ -1026,8 +1015,6 @@ fail_continue("The shared archive file has been truncated."); return false; } - - SharedBaseAddress = header()->shared_base_address(); } return true; @@ -1040,23 +1027,27 @@ } // Read the FileMapInfo information from the file. -bool FileMapInfo::open_for_read(const char* path) { +bool FileMapInfo::open_for_read() { if (_file_open) { return true; } - if (path == NULL) { + if (is_static()) { _full_path = Arguments::GetSharedArchivePath(); } else { - _full_path = path; + _full_path = Arguments::GetSharedDynamicArchivePath(); } int fd = os::open(_full_path, O_RDONLY | O_BINARY, 0); if (fd < 0) { - if (errno == ENOENT) { - // Not locating the shared archive is ok. - fail_continue("Specified shared archive not found (%s).", _full_path); + if (is_static()) { + if (errno == ENOENT) { + // Not locating the shared archive is ok. + fail_continue("Specified shared archive not found (%s).", _full_path); + } else { + fail_continue("Failed to open shared archive file (%s).", + os::strerror(errno)); + } } else { - fail_continue("Failed to open shared archive file (%s).", - os::strerror(errno)); + log_warning(cds, dynamic)("specified dynamic archive doesn't exist: %s", _full_path); } return false; } @@ -1127,25 +1118,35 @@ } } -void FileMapRegion::init(bool is_heap_region, char* base, size_t size, bool read_only, +size_t FileMapRegion::used_aligned() const { + return align_up(used(), os::vm_allocation_granularity()); +} + +void FileMapRegion::init(int region_index, char* base, size_t size, bool read_only, bool allow_exec, int crc) { - _is_heap_region = is_heap_region; + _is_heap_region = HeapShared::is_heap_region(region_index); + _is_bitmap_region = (region_index == MetaspaceShared::bm); + _mapping_offset = 0; - if (is_heap_region) { + if (_is_heap_region) { assert(!DynamicDumpSharedSpaces, "must be"); assert((base - (char*)CompressedKlassPointers::base()) % HeapWordSize == 0, "Sanity"); if (base != NULL) { - _addr._offset = (intx)CompressedOops::encode_not_null((oop)base); - } else { - _addr._offset = 0; + _mapping_offset = (size_t)CompressedOops::encode_not_null((oop)base); + assert(_mapping_offset >> 32 == 0, "must be 32-bit only"); } } else { - _addr._base = base; + if (base != NULL) { + assert(base >= (char*)SharedBaseAddress, "must be"); + _mapping_offset = base - (char*)SharedBaseAddress; + } } _used = size; _read_only = read_only; _allow_exec = allow_exec; _crc = crc; + _mapped_from_file = false; + _mapped_base = NULL; } void FileMapInfo::write_region(int region, char* base, size_t size, @@ -1153,25 +1154,47 @@ Arguments::assert_is_dumping_archive(); FileMapRegion* si = space_at(region); - char* target_base = base; - if (DynamicDumpSharedSpaces) { - assert(!HeapShared::is_heap_region(region), "dynamic archive doesn't support heap regions"); - target_base = DynamicArchive::buffer_to_target(base); + char* target_base; + + if (region == MetaspaceShared::bm) { + target_base = NULL; // always NULL for bm region. + } else { + if (DynamicDumpSharedSpaces) { + assert(!HeapShared::is_heap_region(region), "dynamic archive doesn't support heap regions"); + target_base = DynamicArchive::buffer_to_target(base); + } else { + target_base = base; + } } si->set_file_offset(_file_offset); - log_info(cds)("Shared file region %d: " SIZE_FORMAT_HEX_W(08) + char* requested_base = (target_base == NULL) ? NULL : target_base + MetaspaceShared::final_delta(); + log_info(cds)("Shared file region %d: " SIZE_FORMAT_HEX_W(08) " bytes, addr " INTPTR_FORMAT " file offset " SIZE_FORMAT_HEX_W(08), - region, size, p2i(target_base), _file_offset); + region, size, p2i(requested_base), _file_offset); int crc = ClassLoader::crc32(0, base, (jint)size); - si->init(HeapShared::is_heap_region(region), target_base, size, read_only, allow_exec, crc); + si->init(region, target_base, size, read_only, allow_exec, crc); if (base != NULL) { write_bytes_aligned(base, size); } } + +void FileMapInfo::write_bitmap_region(const CHeapBitMap* ptrmap) { + ResourceMark rm; + size_t size_in_bits = ptrmap->size(); + size_t size_in_bytes = ptrmap->size_in_bytes(); + uintptr_t* buffer = (uintptr_t*)NEW_RESOURCE_ARRAY(char, size_in_bytes); + ptrmap->write_to(buffer, size_in_bytes); + header()->set_ptrmap_size_in_bits(size_in_bits); + + log_info(cds)("ptrmap = " INTPTR_FORMAT " (" SIZE_FORMAT " bytes)", + p2i(buffer), size_in_bytes); + write_region(MetaspaceShared::bm, (char*)buffer, size_in_bytes, /*read_only=*/true, /*allow_exec=*/false); +} + // Write out the given archive heap memory regions. GC code combines multiple // consecutive archive GC regions into one MemRegion whenever possible and // produces the 'heap_mem' array. @@ -1229,11 +1252,13 @@ total_size += size; } - log_info(cds)("Archive heap region %d " INTPTR_FORMAT " - " INTPTR_FORMAT " = " SIZE_FORMAT_W(8) " bytes", + log_info(cds)("Archive heap region %d: " INTPTR_FORMAT " - " INTPTR_FORMAT " = " SIZE_FORMAT_W(8) " bytes", i, p2i(start), p2i(start + size), size); write_region(i, start, size, false, false); if (size > 0) { - space_at(i)->init_oopmap(oopmaps->at(arr_idx)._oopmap, + address oopmap = oopmaps->at(arr_idx)._oopmap; + assert(oopmap >= (address)SharedBaseAddress, "must be"); + space_at(i)->init_oopmap(oopmap - (address)SharedBaseAddress, oopmaps->at(arr_idx)._oopmap_size_in_bits); } } @@ -1285,6 +1310,9 @@ align_file_position(); } +void FileMapInfo::set_final_requested_base(char* b) { + header()->set_final_requested_base(b); +} // Close the shared archive file. This does NOT unmap mapped regions. @@ -1331,94 +1359,197 @@ return true; } -// Map the whole region at once, assumed to be allocated contiguously. -ReservedSpace FileMapInfo::reserve_shared_memory() { - char* requested_addr = region_addr(0); - size_t size = FileMapInfo::core_spaces_size(); - - // Reserve the space first, then map otherwise map will go right over some - // other reserved memory (like the code cache). - ReservedSpace rs(size, os::vm_allocation_granularity(), false, requested_addr); - if (!rs.is_reserved()) { - fail_continue("Unable to reserve shared space at required address " - INTPTR_FORMAT, p2i(requested_addr)); - return rs; - } - // the reserved virtual memory is for mapping class data sharing archive - MemTracker::record_virtual_memory_type((address)rs.base(), mtClassShared); - - return rs; -} - // Memory map a region in the address space. -static const char* shared_region_name[] = { "MiscData", "ReadWrite", "ReadOnly", "MiscCode", +static const char* shared_region_name[] = { "MiscData", "ReadWrite", "ReadOnly", "MiscCode", "Bitmap", "String1", "String2", "OpenArchive1", "OpenArchive2" }; -char* FileMapInfo::map_regions(int regions[], char* saved_base[], size_t len) { - char* prev_top = NULL; - char* curr_base; - char* curr_top; - int i = 0; - for (i = 0; i < (int)len; i++) { - curr_base = map_region(regions[i], &curr_top); - if (curr_base == NULL) { - return NULL; +MapArchiveResult FileMapInfo::map_regions(int regions[], int num_regions, char* mapped_base_address, ReservedSpace rs) { + DEBUG_ONLY(FileMapRegion* last_region = NULL); + intx addr_delta = mapped_base_address - header()->requested_base_address(); + + // Make sure we don't attempt to use header()->mapped_base_address() unless + // it's been successfully mapped. + DEBUG_ONLY(header()->set_mapped_base_address((char*)(uintptr_t)0xdeadbeef);) + + for (int r = 0; r < num_regions; r++) { + int idx = regions[r]; + MapArchiveResult result = map_region(idx, addr_delta, mapped_base_address, rs); + if (result != MAP_ARCHIVE_SUCCESS) { + return result; } - if (i > 0) { - // We require that mc->rw->ro->md to be laid out consecutively, with no - // gaps between them. That way, we can ensure that the OS won't be able to - // allocate any new memory spaces inside _shared_metaspace_{base,top}, which - // would mess up the simple comparision in MetaspaceShared::is_in_shared_metaspace(). - assert(curr_base == prev_top, "must be"); - } - log_info(cds)("Mapped region #%d at base %p top %p", regions[i], curr_base, curr_top); - saved_base[i] = curr_base; - prev_top = curr_top; + FileMapRegion* si = space_at(idx); + DEBUG_ONLY(if (last_region != NULL) { + // Ensure that the OS won't be able to allocate new memory spaces between any mapped + // regions, or else it would mess up the simple comparision in MetaspaceObj::is_shared(). + assert(si->mapped_base() == last_region->mapped_end(), "must have no gaps"); + } + last_region = si;) + log_info(cds)("Mapped %s region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)", is_static() ? "static " : "dynamic", + idx, p2i(si->mapped_base()), p2i(si->mapped_end()), + shared_region_name[idx]); + } - return curr_top; + + DEBUG_ONLY(if (addr_delta == 0 && ArchiveRelocationMode == 1) { + // This is for simulating mmap failures at the requested address. We do it here (instead + // of MetaspaceShared::map_archives) so we can thoroughly test the code for failure handling + // (releasing all allocated resource, etc). + log_info(cds)("ArchiveRelocationMode == 1: always map archive(s) at an alternative address"); + return MAP_ARCHIVE_MMAP_FAILURE; + }); + + header()->set_mapped_base_address(header()->requested_base_address() + addr_delta); + if (addr_delta != 0 && !relocate_pointers(addr_delta)) { + return MAP_ARCHIVE_OTHER_FAILURE; + } + + return MAP_ARCHIVE_SUCCESS; } -char* FileMapInfo::map_region(int i, char** top_ret) { +bool FileMapInfo::read_region(int i, char* base, size_t size) { + assert(MetaspaceShared::use_windows_memory_mapping(), "used by windows only"); + FileMapRegion* si = space_at(i); + log_info(cds)("Commit %s region #%d at base " INTPTR_FORMAT " top " INTPTR_FORMAT " (%s)%s", + is_static() ? "static " : "dynamic", i, p2i(base), p2i(base + size), + shared_region_name[i], si->allow_exec() ? " exec" : ""); + if (!os::commit_memory(base, size, si->allow_exec())) { + log_error(cds)("Failed to commit %s region #%d (%s)", is_static() ? "static " : "dynamic", + i, shared_region_name[i]); + return false; + } + if (lseek(_fd, (long)si->file_offset(), SEEK_SET) != (int)si->file_offset() || + read_bytes(base, size) != size) { + return false; + } + return true; +} + +MapArchiveResult FileMapInfo::map_region(int i, intx addr_delta, char* mapped_base_address, ReservedSpace rs) { assert(!HeapShared::is_heap_region(i), "sanity"); FileMapRegion* si = space_at(i); - size_t used = si->used(); - size_t alignment = os::vm_allocation_granularity(); - size_t size = align_up(used, alignment); - char *requested_addr = region_addr(i); + size_t size = si->used_aligned(); + char *requested_addr = mapped_base_address + si->mapping_offset(); + assert(si->mapped_base() == NULL, "must be not mapped yet"); + assert(requested_addr != NULL, "must be specified"); + + si->set_mapped_from_file(false); -#ifdef _WINDOWS - // Windows cannot remap read-only shared memory to read-write when required for - // RedefineClasses, which is also used by JFR. Always map windows regions as RW. - si->set_read_only(false); -#else - // If a tool agent is in use (debugging enabled), or JFR, we must map the address space RW - if (JvmtiExport::can_modify_any_class() || JvmtiExport::can_walk_any_space() || - Arguments::has_jfr_option()) { + if (MetaspaceShared::use_windows_memory_mapping()) { + // Windows cannot remap read-only shared memory to read-write when required for + // RedefineClasses, which is also used by JFR. Always map windows regions as RW. + si->set_read_only(false); + } else if (JvmtiExport::can_modify_any_class() || JvmtiExport::can_walk_any_space() || + Arguments::has_jfr_option()) { + // If a tool agent is in use (debugging enabled), or JFR, we must map the address space RW si->set_read_only(false); + } else if (addr_delta != 0) { + si->set_read_only(false); // Need to patch the pointers + } + + if (rs.is_reserved()) { + assert(rs.contains(requested_addr) && rs.contains(requested_addr + size - 1), "must be"); + MemTracker::record_virtual_memory_type((address)requested_addr, mtClassShared); } -#endif // _WINDOWS - // map the contents of the CDS archive in this memory - char *base = os::map_memory(_fd, _full_path, si->file_offset(), - requested_addr, size, si->read_only(), - si->allow_exec()); - if (base == NULL || base != requested_addr) { - fail_continue("Unable to map %s shared space at required address.", shared_region_name[i]); - _memory_mapping_failed = true; - return NULL; + if (MetaspaceShared::use_windows_memory_mapping() && addr_delta != 0) { + // This is the second time we try to map the archive(s). We have already created a ReservedSpace + // that covers all the FileMapRegions to ensure all regions can be mapped. However, Windows + // can't mmap into a ReservedSpace, so we just os::read() the data. We're going to patch all the + // regions anyway, so there's no benefit for mmap anyway. + if (!read_region(i, requested_addr, size)) { + return MAP_ARCHIVE_OTHER_FAILURE; // oom or I/O error. + } + } else { + char* base = os::map_memory(_fd, _full_path, si->file_offset(), + requested_addr, size, si->read_only(), + si->allow_exec()); + if (base != requested_addr) { + log_info(cds)("Unable to map %s shared space at required address.", shared_region_name[i]); + _memory_mapping_failed = true; + return MAP_ARCHIVE_MMAP_FAILURE; + } + si->set_mapped_from_file(true); } -#ifdef _WINDOWS - // This call is Windows-only because the memory_type gets recorded for the other platforms - // in method FileMapInfo::reserve_shared_memory(), which is not called on Windows. - MemTracker::record_virtual_memory_type((address)base, mtClassShared); -#endif + si->set_mapped_base(requested_addr); + + if (!rs.is_reserved()) { + // When mapping on Windows with (addr_delta == 0), we don't reserve the address space for the regions + // (Windows can't mmap into a ReservedSpace). In this case, NMT requires we call it after + // os::map_memory has succeeded. + assert(MetaspaceShared::use_windows_memory_mapping(), "Windows memory mapping only"); + MemTracker::record_virtual_memory_type((address)requested_addr, mtClassShared); + } if (VerifySharedSpaces && !verify_region_checksum(i)) { + return MAP_ARCHIVE_OTHER_FAILURE; + } + + return MAP_ARCHIVE_SUCCESS; +} + +char* FileMapInfo::map_relocation_bitmap(size_t& bitmap_size) { + FileMapRegion* si = space_at(MetaspaceShared::bm); + bitmap_size = si->used_aligned(); + bool read_only = true, allow_exec = false; + char* requested_addr = NULL; // allow OS to pick any location + char* bitmap_base = os::map_memory(_fd, _full_path, si->file_offset(), + requested_addr, bitmap_size, read_only, allow_exec); + if (bitmap_base == NULL) { + log_error(cds)("failed to map relocation bitmap"); return NULL; } - *top_ret = base + size; - return base; + if (VerifySharedSpaces && !region_crc_check(bitmap_base, bitmap_size, si->crc())) { + log_error(cds)("relocation bitmap CRC error"); + if (!os::unmap_memory(bitmap_base, bitmap_size)) { + fatal("os::unmap_memory of relocation bitmap failed"); + } + return NULL; + } + + return bitmap_base; +} + +bool FileMapInfo::relocate_pointers(intx addr_delta) { + log_debug(cds, reloc)("runtime archive relocation start"); + size_t bitmap_size; + char* bitmap_base = map_relocation_bitmap(bitmap_size); + + if (bitmap_base == NULL) { + return false; + } else { + size_t ptrmap_size_in_bits = header()->ptrmap_size_in_bits(); + log_debug(cds, reloc)("mapped relocation bitmap @ " INTPTR_FORMAT " (" SIZE_FORMAT + " bytes = " SIZE_FORMAT " bits)", + p2i(bitmap_base), bitmap_size, ptrmap_size_in_bits); + + BitMapView ptrmap((BitMap::bm_word_t*)bitmap_base, ptrmap_size_in_bits); + + // Patch all pointers in the the mapped region that are marked by ptrmap. + address patch_base = (address)mapped_base(); + address patch_end = (address)mapped_end(); + + // the current value of the pointers to be patched must be within this + // range (i.e., must be between the requesed base address, and the of the current archive). + // Note: top archive may point to objects in the base archive, but not the other way around. + address valid_old_base = (address)header()->requested_base_address(); + address valid_old_end = valid_old_base + mapping_end_offset(); + + // after patching, the pointers must point inside this range + // (the requested location of the archive, as mapped at runtime). + address valid_new_base = (address)header()->mapped_base_address(); + address valid_new_end = (address)mapped_end(); + + SharedDataRelocator patcher((address*)patch_base, (address*)patch_end, valid_old_base, valid_old_end, + valid_new_base, valid_new_end, addr_delta); + ptrmap.iterate(&patcher); + + if (!os::unmap_memory(bitmap_base, bitmap_size)) { + fatal("os::unmap_memory of relocation bitmap failed"); + } + log_debug(cds, reloc)("runtime archive relocation done"); + return true; + } } size_t FileMapInfo::read_bytes(void* buffer, size_t count) { @@ -1434,10 +1565,13 @@ } address FileMapInfo::decode_start_address(FileMapRegion* spc, bool with_current_oop_encoding_mode) { + size_t offset = spc->mapping_offset(); + assert((offset >> 32) == 0, "must be 32-bit only"); + uint n = (uint)offset; if (with_current_oop_encoding_mode) { - return (address)CompressedOops::decode_not_null(spc->offset()); + return (address)CompressedOops::decode_not_null(n); } else { - return (address)HeapShared::decode_from_archive(spc->offset()); + return (address)HeapShared::decode_from_archive(n); } } @@ -1705,7 +1839,7 @@ int first_region_idx) { for (int i=0; ioopmap(), + HeapShared::patch_archived_heap_embedded_pointers(ranges[i], (address)(SharedBaseAddress + si->oopmap_offset()), si->oopmap_size_in_bits()); } } @@ -1759,11 +1893,10 @@ } } -void FileMapInfo::unmap_regions(int regions[], char* saved_base[], size_t len) { - for (int i = 0; i < (int)len; i++) { - if (saved_base[i] != NULL) { - unmap_region(regions[i]); - } +void FileMapInfo::unmap_regions(int regions[], int num_regions) { + for (int r = 0; r < num_regions; r++) { + int idx = regions[r]; + unmap_region(idx); } } @@ -1772,16 +1905,17 @@ void FileMapInfo::unmap_region(int i) { assert(!HeapShared::is_heap_region(i), "sanity"); FileMapRegion* si = space_at(i); + char* mapped_base = si->mapped_base(); size_t used = si->used(); size_t size = align_up(used, os::vm_allocation_granularity()); - if (used == 0) { - return; - } - - char* addr = region_addr(i); - if (!os::unmap_memory(addr, size)) { - fail_stop("Unable to unmap shared space."); + if (mapped_base != NULL && size > 0 && si->mapped_from_file()) { + log_info(cds)("Unmapping region #%d at base " INTPTR_FORMAT " (%s)", i, p2i(mapped_base), + shared_region_name[i]); + if (!os::unmap_memory(mapped_base, size)) { + fatal("os::unmap_memory failed"); + } + si->set_mapped_base(NULL); } } @@ -1813,7 +1947,7 @@ // [1] validate_header() - done here. // [2] validate_shared_path_table - this is done later, because the table is in the RW // region of the archive, which is not mapped yet. -bool FileMapInfo::initialize(bool is_static) { +bool FileMapInfo::initialize() { assert(UseSharedSpaces, "UseSharedSpaces expected."); if (JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()) { @@ -1828,11 +1962,10 @@ if (!open_for_read()) { return false; } - - init_from_file(_fd, is_static); - // UseSharedSpaces could be disabled if the checking of some of the header fields in - // init_from_file has failed. - if (!UseSharedSpaces || !validate_header(is_static)) { + if (!init_from_file(_fd)) { + return false; + } + if (!validate_header()) { return false; } return true; @@ -1845,10 +1978,18 @@ return si->used() > 0 ? (char*)start_address_as_decoded_with_current_oop_encoding_mode(si) : NULL; } else { - return si->base(); + return si->mapped_base(); } } +FileMapRegion* FileMapInfo::first_core_space() const { + return is_static() ? space_at(MetaspaceShared::mc) : space_at(MetaspaceShared::rw); +} + +FileMapRegion* FileMapInfo::last_core_space() const { + return is_static() ? space_at(MetaspaceShared::md) : space_at(MetaspaceShared::mc); +} + int FileMapHeader::compute_crc() { char* start = (char*)this; // start computing from the field after _crc @@ -1860,7 +2001,6 @@ // This function should only be called during run time with UseSharedSpaces enabled. bool FileMapHeader::validate() { - if (_obj_alignment != ObjectAlignmentInBytes) { FileMapInfo::fail_continue("The shared archive file's ObjectAlignmentInBytes of %d" " does not equal the current ObjectAlignmentInBytes of " INTX_FORMAT ".", @@ -1913,7 +2053,7 @@ return true; } -bool FileMapInfo::validate_header(bool is_static) { +bool FileMapInfo::validate_header() { return header()->validate(); } @@ -1932,18 +2072,14 @@ // Unmap mapped regions of shared space. void FileMapInfo::stop_sharing_and_unmap(const char* msg) { - MetaspaceShared::set_shared_metaspace_range(NULL, NULL); + MetaspaceShared::set_shared_metaspace_range(NULL, NULL, NULL); FileMapInfo *map_info = FileMapInfo::current_info(); if (map_info) { map_info->fail_continue("%s", msg); for (int i = 0; i < MetaspaceShared::num_non_heap_spaces; i++) { if (!HeapShared::is_heap_region(i)) { - char *addr = map_info->region_addr(i); - if (addr != NULL) { - map_info->unmap_region(i); - map_info->space_at(i)->mark_invalid(); - } + map_info->unmap_region(i); } } // Dealloc the archive heap regions only without unmapping. The regions are part diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/filemap.hpp --- a/src/hotspot/share/memory/filemap.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/filemap.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -43,6 +43,8 @@ static const int JVM_IDENT_MAX = 256; +class CHeapBitMap; + class SharedClassPathEntry { enum { modules_image_entry, @@ -104,6 +106,9 @@ Array* _table; int _size; public: + SharedPathTable() : _table(NULL), _size(0) {} + SharedPathTable(Array* table, int size) : _table(table), _size(size) {} + void dumptime_init(ClassLoaderData* loader_data, Thread* THREAD); void metaspace_pointers_do(MetaspaceClosure* it); @@ -138,25 +143,29 @@ } // Accessors - int crc() const { return _crc; } - size_t file_offset() const { return _file_offset; } - char* base() const { assert_is_not_heap_region(); return _addr._base; } - narrowOop offset() const { assert_is_heap_region(); return (narrowOop)(_addr._offset); } - size_t used() const { return _used; } - bool read_only() const { return _read_only != 0; } - bool allow_exec() const { return _allow_exec != 0; } - void* oopmap() const { return _oopmap; } - size_t oopmap_size_in_bits() const { return _oopmap_size_in_bits; } + int crc() const { return _crc; } + size_t file_offset() const { return _file_offset; } + size_t mapping_offset() const { return _mapping_offset; } + size_t mapping_end_offset() const { return _mapping_offset + used_aligned(); } + size_t used() const { return _used; } + size_t used_aligned() const; // aligned up to os::vm_allocation_granularity() + char* mapped_base() const { assert_is_not_heap_region(); return _mapped_base; } + char* mapped_end() const { return mapped_base() + used_aligned(); } + bool read_only() const { return _read_only != 0; } + bool allow_exec() const { return _allow_exec != 0; } + bool mapped_from_file() const { return _mapped_from_file != 0; } + size_t oopmap_offset() const { assert_is_heap_region(); return _oopmap_offset; } + size_t oopmap_size_in_bits() const { assert_is_heap_region(); return _oopmap_size_in_bits; } - void set_file_offset(size_t s) { _file_offset = s; } - void set_read_only(bool v) { _read_only = v; } - void mark_invalid() { _addr._base = NULL; } - - void init(bool is_heap_region, char* base, size_t size, bool read_only, + void set_file_offset(size_t s) { _file_offset = s; } + void set_read_only(bool v) { _read_only = v; } + void set_mapped_base(char* p) { _mapped_base = p; } + void set_mapped_from_file(bool v) { _mapped_from_file = v; } + void init(int region_index, char* base, size_t size, bool read_only, bool allow_exec, int crc); - void init_oopmap(void* map, size_t size_in_bits) { - _oopmap = map; + void init_oopmap(size_t oopmap_offset, size_t size_in_bits) { + _oopmap_offset = oopmap_offset; _oopmap_size_in_bits = size_in_bits; } }; @@ -178,13 +187,10 @@ uintx _max_heap_size; // java max heap size during dumping CompressedOops::Mode _narrow_oop_mode; // compressed oop encoding mode int _narrow_klass_shift; // save narrow klass base and shift - address _narrow_klass_base; - char* _misc_data_patching_start; - char* _serialized_data_start; // Data accessed using {ReadClosure,WriteClosure}::serialize() - address _i2i_entry_code_buffers; + size_t _misc_data_patching_offset; + size_t _serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize() + size_t _i2i_entry_code_buffers_offset; size_t _i2i_entry_code_buffers_size; - size_t _core_spaces_size; // number of bytes allocated by the core spaces - // (mc, md, ro, rw and od). address _heap_end; // heap end at dump time. bool _base_archive_is_default; // indicates if the base archive is the system default one @@ -202,7 +208,8 @@ // check_nonempty_dir_in_shared_path_table() // validate_shared_path_table() // validate_non_existent_class_paths() - SharedPathTable _shared_path_table; + size_t _shared_path_table_offset; + int _shared_path_table_size; jshort _app_class_paths_start_index; // Index of first app classpath entry jshort _app_module_paths_start_index; // Index of first module path entry @@ -211,9 +218,19 @@ bool _verify_local; // BytecodeVerificationLocal setting bool _verify_remote; // BytecodeVerificationRemote setting bool _has_platform_or_app_classes; // Archive contains app classes - size_t _shared_base_address; // SharedBaseAddress used at dump time + char* _requested_base_address; // Archive relocation is not necessary if we map with this base address. + char* _mapped_base_address; // Actual base address where archive is mapped. + bool _allow_archiving_with_java_agent; // setting of the AllowArchivingWithJavaAgent option + size_t _ptrmap_size_in_bits; // Size of pointer relocation bitmap + char* from_mapped_offset(size_t offset) const { + return mapped_base_address() + offset; + } + void set_mapped_offset(char* p, size_t *offset) { + assert(p >= mapped_base_address(), "sanity"); + *offset = p - mapped_base_address(); + } public: // Accessors -- fields declared in CDSFileMapHeaderBase unsigned int magic() const {return _magic;} @@ -234,19 +251,19 @@ uintx max_heap_size() const { return _max_heap_size; } CompressedOops::Mode narrow_oop_mode() const { return _narrow_oop_mode; } int narrow_klass_shift() const { return _narrow_klass_shift; } - address narrow_klass_base() const { return _narrow_klass_base; } - char* misc_data_patching_start() const { return _misc_data_patching_start; } - char* serialized_data_start() const { return _serialized_data_start; } - address i2i_entry_code_buffers() const { return _i2i_entry_code_buffers; } + address narrow_klass_base() const { return (address)mapped_base_address(); } + char* misc_data_patching_start() const { return from_mapped_offset(_misc_data_patching_offset); } + char* serialized_data_start() const { return from_mapped_offset(_serialized_data_offset); } + address i2i_entry_code_buffers() const { return (address)from_mapped_offset(_i2i_entry_code_buffers_offset); } size_t i2i_entry_code_buffers_size() const { return _i2i_entry_code_buffers_size; } - size_t core_spaces_size() const { return _core_spaces_size; } address heap_end() const { return _heap_end; } bool base_archive_is_default() const { return _base_archive_is_default; } const char* jvm_ident() const { return _jvm_ident; } size_t base_archive_name_size() const { return _base_archive_name_size; } - size_t shared_base_address() const { return _shared_base_address; } + char* requested_base_address() const { return _requested_base_address; } + char* mapped_base_address() const { return _mapped_base_address; } bool has_platform_or_app_classes() const { return _has_platform_or_app_classes; } - SharedPathTable shared_path_table() const { return _shared_path_table; } + size_t ptrmap_size_in_bits() const { return _ptrmap_size_in_bits; } // FIXME: These should really return int jshort max_used_path_index() const { return _max_used_path_index; } @@ -254,27 +271,32 @@ jshort app_class_paths_start_index() const { return _app_class_paths_start_index; } jshort num_module_paths() const { return _num_module_paths; } - void set_core_spaces_size(size_t s) { _core_spaces_size = s; } void set_has_platform_or_app_classes(bool v) { _has_platform_or_app_classes = v; } - void set_misc_data_patching_start(char* p) { _misc_data_patching_start = p; } - void set_serialized_data_start(char* p) { _serialized_data_start = p; } + void set_misc_data_patching_start(char* p) { set_mapped_offset(p, &_misc_data_patching_offset); } + void set_serialized_data_start(char* p) { set_mapped_offset(p, &_serialized_data_offset); } void set_base_archive_name_size(size_t s) { _base_archive_name_size = s; } void set_base_archive_is_default(bool b) { _base_archive_is_default = b; } void set_header_size(size_t s) { _header_size = s; } - + void set_ptrmap_size_in_bits(size_t s) { _ptrmap_size_in_bits = s; } + void set_mapped_base_address(char* p) { _mapped_base_address = p; } void set_i2i_entry_code_buffers(address p, size_t s) { - _i2i_entry_code_buffers = p; + set_mapped_offset((char*)p, &_i2i_entry_code_buffers_offset); _i2i_entry_code_buffers_size = s; } - void relocate_shared_path_table(Array* t) { - assert(DynamicDumpSharedSpaces, "only"); - _shared_path_table.set_table(t); + void set_shared_path_table(SharedPathTable table) { + set_mapped_offset((char*)table.table(), &_shared_path_table_offset); + _shared_path_table_size = table.size(); } - void shared_path_table_metaspace_pointers_do(MetaspaceClosure* it) { - assert(DynamicDumpSharedSpaces, "only"); - _shared_path_table.metaspace_pointers_do(it); + void set_final_requested_base(char* b) { + _requested_base_address = b; + _mapped_base_address = 0; + } + + SharedPathTable shared_path_table() const { + return SharedPathTable((Array*)from_mapped_offset(_shared_path_table_offset), + _shared_path_table_size); } bool validate(); @@ -301,6 +323,7 @@ bool _is_static; bool _file_open; + bool _is_mapped; int _fd; size_t _file_offset; const char* _full_path; @@ -327,8 +350,11 @@ static bool get_base_archive_name_from_header(const char* archive_name, int* size, char** base_archive_name); static bool check_archive(const char* archive_name, bool is_static); + static SharedPathTable shared_path_table() { + return _shared_path_table; + } void restore_shared_path_table(); - bool init_from_file(int fd, bool is_static); + bool init_from_file(int fd); static void metaspace_pointers_do(MetaspaceClosure* it); void log_paths(const char* msg, int start_idx, int end_idx); @@ -341,7 +367,7 @@ void set_header_crc(int crc) { header()->set_crc(crc); } int space_crc(int i) const { return space_at(i)->crc(); } void populate_header(size_t alignment); - bool validate_header(bool is_static); + bool validate_header(); void invalidate(); int crc() const { return header()->crc(); } int version() const { return header()->version(); } @@ -370,11 +396,17 @@ header()->set_i2i_entry_code_buffers(addr, s); } - void set_core_spaces_size(size_t s) const { header()->set_core_spaces_size(s); } - size_t core_spaces_size() const { return header()->core_spaces_size(); } + bool is_static() const { return _is_static; } + bool is_mapped() const { return _is_mapped; } + void set_is_mapped(bool v) { _is_mapped = v; } + const char* full_path() const { return _full_path; } + void set_final_requested_base(char* b); + + char* requested_base_address() const { return header()->requested_base_address(); } + class DynamicArchiveHeader* dynamic_header() const { - assert(!_is_static, "must be"); + assert(!is_static(), "must be"); return (DynamicArchiveHeader*)header(); } @@ -402,21 +434,21 @@ static void assert_mark(bool check); // File manipulation. - bool initialize(bool is_static) NOT_CDS_RETURN_(false); - bool open_for_read(const char* path = NULL); + bool initialize() NOT_CDS_RETURN_(false); + bool open_for_read(); void open_for_write(const char* path = NULL); void write_header(); void write_region(int region, char* base, size_t size, bool read_only, bool allow_exec); + void write_bitmap_region(const CHeapBitMap* ptrmap); size_t write_archive_heap_regions(GrowableArray *heap_mem, GrowableArray *oopmaps, int first_region_id, int max_num_regions); void write_bytes(const void* buffer, size_t count); void write_bytes_aligned(const void* buffer, size_t count); size_t read_bytes(void* buffer, size_t count); - char* map_regions(int regions[], char* saved_base[], size_t len); - char* map_region(int i, char** top_ret); - void map_heap_regions_impl() NOT_CDS_JAVA_HEAP_RETURN; + MapArchiveResult map_regions(int regions[], int num_regions, char* mapped_base_address, ReservedSpace rs); + void unmap_regions(int regions[], int num_regions); void map_heap_regions() NOT_CDS_JAVA_HEAP_RETURN; void fixup_mapped_heap_regions() NOT_CDS_JAVA_HEAP_RETURN; void patch_archived_heap_embedded_pointers() NOT_CDS_JAVA_HEAP_RETURN; @@ -424,7 +456,6 @@ int first_region_idx) NOT_CDS_JAVA_HEAP_RETURN; bool has_heap_regions() NOT_CDS_JAVA_HEAP_RETURN_(false); MemRegion get_heap_regions_range_with_current_oop_encoding_mode() NOT_CDS_JAVA_HEAP_RETURN_(MemRegion()); - void unmap_regions(int regions[], char* saved_base[], size_t len); void unmap_region(int i); bool verify_region_checksum(int i); void close(); @@ -452,6 +483,9 @@ static void check_nonempty_dir_in_shared_path_table(); bool validate_shared_path_table(); void validate_non_existent_class_paths(); + static void set_shared_path_table(FileMapInfo* info) { + _shared_path_table = info->header()->shared_path_table(); + } static void update_jar_manifest(ClassPathEntry *cpe, SharedClassPathEntry* ent, TRAPS); static int num_non_existent_class_paths(); static void record_non_existent_class_path_entry(const char* path); @@ -475,12 +509,28 @@ char* region_addr(int idx); + // The offset of the first core region in the archive, relative to SharedBaseAddress + size_t mapping_base_offset() const { return first_core_space()->mapping_offset(); } + // The offset of the (exclusive) end of the last core region in this archive, relative to SharedBaseAddress + size_t mapping_end_offset() const { return last_core_space()->mapping_end_offset(); } + + char* mapped_base() const { return first_core_space()->mapped_base(); } + char* mapped_end() const { return last_core_space()->mapped_end(); } + + // Non-zero if the archive needs to be mapped a non-default location due to ASLR. + intx relocation_delta() const { + return header()->mapped_base_address() - header()->requested_base_address(); + } + + FileMapRegion* first_core_space() const; + FileMapRegion* last_core_space() const; + private: void seek_to_position(size_t pos); char* skip_first_path_entry(const char* path) NOT_CDS_RETURN_(NULL); int num_paths(const char* path) NOT_CDS_RETURN_(0); GrowableArray* create_path_array(const char* path) NOT_CDS_RETURN_(NULL); - bool fail(const char* msg, const char* name) NOT_CDS_RETURN_(false); + bool classpath_failure(const char* msg, const char* name) NOT_CDS_RETURN_(false); bool check_paths(int shared_path_start_idx, int num_paths, GrowableArray* rp_array) NOT_CDS_RETURN_(false); bool validate_boot_class_paths() NOT_CDS_RETURN_(false); @@ -489,6 +539,11 @@ bool is_open = false) NOT_CDS_JAVA_HEAP_RETURN_(false); bool region_crc_check(char* buf, size_t size, int expected_crc) NOT_CDS_RETURN_(false); void dealloc_archive_heap_regions(MemRegion* regions, int num, bool is_open) NOT_CDS_JAVA_HEAP_RETURN; + void map_heap_regions_impl() NOT_CDS_JAVA_HEAP_RETURN; + char* map_relocation_bitmap(size_t& bitmap_size); + MapArchiveResult map_region(int i, intx addr_delta, char* mapped_base_address, ReservedSpace rs); + bool read_region(int i, char* base, size_t size); + bool relocate_pointers(intx addr_delta); FileMapRegion* space_at(int i) const { return header()->space_at(i); diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/heapShared.cpp --- a/src/hotspot/share/memory/heapShared.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/heapShared.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -26,15 +26,18 @@ #include "classfile/javaClasses.inline.hpp" #include "classfile/stringTable.hpp" #include "classfile/symbolTable.hpp" +#include "classfile/systemDictionaryShared.hpp" #include "classfile/vmSymbols.hpp" #include "logging/log.hpp" #include "logging/logMessage.hpp" #include "logging/logStream.hpp" +#include "memory/archiveUtils.hpp" #include "memory/filemap.hpp" #include "memory/heapShared.inline.hpp" #include "memory/iterator.inline.hpp" #include "memory/metadataFactory.hpp" #include "memory/metaspaceClosure.hpp" +#include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" #include "oops/compressedOops.inline.hpp" @@ -383,8 +386,13 @@ _k->external_name(), i, subgraph_k->external_name()); } _subgraph_object_klasses->at_put(i, subgraph_k); + ArchivePtrMarker::mark_pointer(_subgraph_object_klasses->adr_at(i)); } } + + ArchivePtrMarker::mark_pointer(&_k); + ArchivePtrMarker::mark_pointer(&_entry_field_records); + ArchivePtrMarker::mark_pointer(&_subgraph_object_klasses); } struct CopyKlassSubGraphInfoToArchive : StackObj { @@ -397,7 +405,7 @@ (ArchivedKlassSubGraphInfoRecord*)MetaspaceShared::read_only_space_alloc(sizeof(ArchivedKlassSubGraphInfoRecord)); record->init(&info); - unsigned int hash = primitive_hash(klass); + unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary(klass); u4 delta = MetaspaceShared::object_delta_u4(record); _writer->add(hash, delta); } @@ -436,7 +444,7 @@ } assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces"); - unsigned int hash = primitive_hash(k); + unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary(k); const ArchivedKlassSubGraphInfoRecord* record = _run_time_subgraph_info_table.lookup(k, hash, 0); // Initialize from archived data. Currently this is done only @@ -606,8 +614,20 @@ assert(orig_obj != NULL, "must be"); assert(!is_archived_object(orig_obj), "sanity"); - // java.lang.Class instances cannot be included in an archived - // object sub-graph. + if (!JavaClasses::is_supported_for_archiving(orig_obj)) { + // This object has injected fields that cannot be supported easily, so we disallow them for now. + // If you get an error here, you probably made a change in the JDK library that has added + // these objects that are referenced (directly or indirectly) by static fields. + ResourceMark rm; + log_error(cds, heap)("Cannot archive object of class %s", orig_obj->klass()->external_name()); + vm_exit(1); + } + + // java.lang.Class instances cannot be included in an archived object sub-graph. We only support + // them as Klass::_archived_mirror because they need to be specially restored at run time. + // + // If you get an error here, you probably made a change in the JDK library that has added a Class + // object that is referenced (directly or indirectly) by static fields. if (java_lang_Class::is_instance(orig_obj)) { log_error(cds, heap)("(%d) Unknown java.lang.Class object is in the archived sub-graph", level); vm_exit(1); diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/metaspace.cpp --- a/src/hotspot/share/memory/metaspace.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/metaspace.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -975,25 +975,18 @@ #ifdef _LP64 static const uint64_t UnscaledClassSpaceMax = (uint64_t(max_juint) + 1); -void Metaspace::set_narrow_klass_base_and_shift(address metaspace_base, address cds_base) { +void Metaspace::set_narrow_klass_base_and_shift(ReservedSpace metaspace_rs, address cds_base) { assert(!DumpSharedSpaces, "narrow_klass is set by MetaspaceShared class."); // Figure out the narrow_klass_base and the narrow_klass_shift. The // narrow_klass_base is the lower of the metaspace base and the cds base // (if cds is enabled). The narrow_klass_shift depends on the distance // between the lower base and higher address. - address lower_base; - address higher_address; -#if INCLUDE_CDS - if (UseSharedSpaces) { - higher_address = MAX2((address)(cds_base + MetaspaceShared::core_spaces_size()), - (address)(metaspace_base + compressed_class_space_size())); - lower_base = MIN2(metaspace_base, cds_base); - } else -#endif - { - higher_address = metaspace_base + compressed_class_space_size(); - lower_base = metaspace_base; - + address lower_base = (address)metaspace_rs.base(); + address higher_address = (address)metaspace_rs.end(); + if (cds_base != NULL) { + assert(UseSharedSpaces, "must be"); + lower_base = MIN2(lower_base, cds_base); + } else { uint64_t klass_encoding_max = UnscaledClassSpaceMax << LogKlassAlignmentInBytes; // If compressed class space fits in lower 32G, we don't need a base. if (higher_address <= (address)klass_encoding_max) { @@ -1018,21 +1011,8 @@ AOTLoader::set_narrow_klass_shift(); } -#if INCLUDE_CDS -// Return TRUE if the specified metaspace_base and cds_base are close enough -// to work with compressed klass pointers. -bool Metaspace::can_use_cds_with_metaspace_addr(char* metaspace_base, address cds_base) { - assert(cds_base != 0 && UseSharedSpaces, "Only use with CDS"); - assert(UseCompressedClassPointers, "Only use with CompressedKlassPtrs"); - address lower_base = MIN2((address)metaspace_base, cds_base); - address higher_address = MAX2((address)(cds_base + MetaspaceShared::core_spaces_size()), - (address)(metaspace_base + compressed_class_space_size())); - return ((uint64_t)(higher_address - lower_base) <= UnscaledClassSpaceMax); -} -#endif - // Try to allocate the metaspace at the requested addr. -void Metaspace::allocate_metaspace_compressed_klass_ptrs(char* requested_addr, address cds_base) { +void Metaspace::allocate_metaspace_compressed_klass_ptrs(ReservedSpace metaspace_rs, char* requested_addr, address cds_base) { assert(!DumpSharedSpaces, "compress klass space is allocated by MetaspaceShared class."); assert(using_class_space(), "called improperly"); assert(UseCompressedClassPointers, "Only use with CompressedKlassPtrs"); @@ -1045,14 +1025,16 @@ // Don't use large pages for the class space. bool large_pages = false; + if (metaspace_rs.is_reserved()) { + // CDS should have already reserved the space. + assert(requested_addr == NULL, "not used"); + assert(cds_base != NULL, "CDS should have already reserved the memory space"); + } else { + assert(cds_base == NULL, "must be"); #if !(defined(AARCH64) || defined(AIX)) - ReservedSpace metaspace_rs = ReservedSpace(compressed_class_space_size(), - _reserve_alignment, - large_pages, - requested_addr); + metaspace_rs = ReservedSpace(compressed_class_space_size(), _reserve_alignment, + large_pages, requested_addr); #else // AARCH64 - ReservedSpace metaspace_rs; - // Our compressed klass pointers may fit nicely into the lower 32 // bits. if ((uint64_t)requested_addr + compressed_class_space_size() < 4*G) { @@ -1077,19 +1059,6 @@ increment = 4*G; } -#if INCLUDE_CDS - if (UseSharedSpaces - && ! can_use_cds_with_metaspace_addr(a, cds_base)) { - // We failed to find an aligned base that will reach. Fall - // back to using our requested addr. - metaspace_rs = ReservedSpace(compressed_class_space_size(), - _reserve_alignment, - large_pages, - requested_addr); - break; - } -#endif - metaspace_rs = ReservedSpace(compressed_class_space_size(), _reserve_alignment, large_pages, @@ -1098,53 +1067,30 @@ break; } } - #endif // AARCH64 + } if (!metaspace_rs.is_reserved()) { -#if INCLUDE_CDS - if (UseSharedSpaces) { - size_t increment = align_up(1*G, _reserve_alignment); - - // Keep trying to allocate the metaspace, increasing the requested_addr - // by 1GB each time, until we reach an address that will no longer allow - // use of CDS with compressed klass pointers. - char *addr = requested_addr; - while (!metaspace_rs.is_reserved() && (addr + increment > addr) && - can_use_cds_with_metaspace_addr(addr + increment, cds_base)) { - addr = addr + increment; - metaspace_rs = ReservedSpace(compressed_class_space_size(), - _reserve_alignment, large_pages, addr); - } - } -#endif + assert(cds_base == NULL, "CDS should have already reserved the memory space"); // If no successful allocation then try to allocate the space anywhere. If // that fails then OOM doom. At this point we cannot try allocating the // metaspace as if UseCompressedClassPointers is off because too much // initialization has happened that depends on UseCompressedClassPointers. // So, UseCompressedClassPointers cannot be turned off at this point. + metaspace_rs = ReservedSpace(compressed_class_space_size(), + _reserve_alignment, large_pages); if (!metaspace_rs.is_reserved()) { - metaspace_rs = ReservedSpace(compressed_class_space_size(), - _reserve_alignment, large_pages); - if (!metaspace_rs.is_reserved()) { - vm_exit_during_initialization(err_msg("Could not allocate metaspace: " SIZE_FORMAT " bytes", - compressed_class_space_size())); - } + vm_exit_during_initialization(err_msg("Could not allocate metaspace: " SIZE_FORMAT " bytes", + compressed_class_space_size())); } } - // If we got here then the metaspace got allocated. - MemTracker::record_virtual_memory_type((address)metaspace_rs.base(), mtClass); + if (cds_base == NULL) { + // If we got here then the metaspace got allocated. + MemTracker::record_virtual_memory_type((address)metaspace_rs.base(), mtClass); + } -#if INCLUDE_CDS - // Verify that we can use shared spaces. Otherwise, turn off CDS. - if (UseSharedSpaces && !can_use_cds_with_metaspace_addr(metaspace_rs.base(), cds_base)) { - FileMapInfo::stop_sharing_and_unmap( - "Could not allocate metaspace at a compatible address"); - } -#endif - set_narrow_klass_base_and_shift((address)metaspace_rs.base(), - UseSharedSpaces ? (address)cds_base : 0); + set_narrow_klass_base_and_shift(metaspace_rs, cds_base); initialize_class_space(metaspace_rs); @@ -1247,31 +1193,30 @@ void Metaspace::global_initialize() { MetaspaceGC::initialize(); + bool class_space_inited = false; #if INCLUDE_CDS if (DumpSharedSpaces) { MetaspaceShared::initialize_dumptime_shared_and_meta_spaces(); + class_space_inited = true; } else if (UseSharedSpaces) { // If any of the archived space fails to map, UseSharedSpaces - // is reset to false. Fall through to the - // (!DumpSharedSpaces && !UseSharedSpaces) case to set up class - // metaspace. + // is reset to false. MetaspaceShared::initialize_runtime_shared_and_meta_spaces(); + class_space_inited = UseSharedSpaces; } if (DynamicDumpSharedSpaces && !UseSharedSpaces) { vm_exit_during_initialization("DynamicDumpSharedSpaces is unsupported when base CDS archive is not loaded", NULL); } +#endif // INCLUDE_CDS - if (!DumpSharedSpaces && !UseSharedSpaces) -#endif // INCLUDE_CDS - { #ifdef _LP64 - if (using_class_space()) { - char* base = (char*)align_up(CompressedOops::end(), _reserve_alignment); - allocate_metaspace_compressed_klass_ptrs(base, 0); - } -#endif // _LP64 + if (using_class_space() && !class_space_inited) { + char* base = (char*)align_up(CompressedOops::end(), _reserve_alignment); + ReservedSpace dummy; + allocate_metaspace_compressed_klass_ptrs(dummy, base, 0); } +#endif // Initialize these before initializing the VirtualSpaceList _first_chunk_word_size = InitialBootClassLoaderMetaspaceSize / BytesPerWord; diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/metaspace.hpp --- a/src/hotspot/share/memory/metaspace.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/metaspace.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -172,16 +172,13 @@ assert(!_frozen, "sanity"); } #ifdef _LP64 - static void allocate_metaspace_compressed_klass_ptrs(char* requested_addr, address cds_base); + static void allocate_metaspace_compressed_klass_ptrs(ReservedSpace metaspace_rs, char* requested_addr, address cds_base); #endif private: #ifdef _LP64 - static void set_narrow_klass_base_and_shift(address metaspace_base, address cds_base); - - // Returns true if can use CDS with metaspace allocated as specified address. - static bool can_use_cds_with_metaspace_addr(char* metaspace_base, address cds_base); + static void set_narrow_klass_base_and_shift(ReservedSpace metaspace_rs, address cds_base); static void initialize_class_space(ReservedSpace rs); #endif diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/metaspaceClosure.hpp --- a/src/hotspot/share/memory/metaspaceClosure.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/metaspaceClosure.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -75,6 +75,10 @@ _default }; + enum SpecialRef { + _method_entry_ref + }; + // class MetaspaceClosure::Ref -- // // MetaspaceClosure can be viewed as a very simple type of copying garbage @@ -278,6 +282,16 @@ template void push(T** mpp, Writability w = _default) { push_impl(new ObjectRef(mpp, w)); } + + template void push_method_entry(T** mpp, intptr_t* p) { + push_special(_method_entry_ref, new ObjectRef(mpp, _default), (intptr_t*)p); + } + + // This is for tagging special pointers that are not a reference to MetaspaceObj. It's currently + // used to mark the method entry points in Method/ConstMethod. + virtual void push_special(SpecialRef type, Ref* obj, intptr_t* p) { + assert(type == _method_entry_ref, "only special type allowed for now"); + } }; // This is a special MetaspaceClosure that visits each unique MetaspaceObj once. diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/metaspaceShared.cpp --- a/src/hotspot/share/memory/metaspaceShared.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/metaspaceShared.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -41,6 +41,8 @@ #include "interpreter/bytecodes.hpp" #include "logging/log.hpp" #include "logging/logMessage.hpp" +#include "memory/archiveUtils.inline.hpp" +#include "memory/dynamicArchive.hpp" #include "memory/filemap.hpp" #include "memory/heapShared.inline.hpp" #include "memory/metaspace.hpp" @@ -48,7 +50,6 @@ #include "memory/metaspaceShared.hpp" #include "memory/resourceArea.hpp" #include "memory/universe.hpp" -#include "memory/dynamicArchive.hpp" #include "oops/compressedOops.inline.hpp" #include "oops/instanceClassLoaderKlass.hpp" #include "oops/instanceMirrorKlass.hpp" @@ -67,7 +68,7 @@ #include "runtime/vmThread.hpp" #include "runtime/vmOperations.hpp" #include "utilities/align.hpp" -#include "utilities/bitMap.hpp" +#include "utilities/bitMap.inline.hpp" #include "utilities/defaultStream.hpp" #include "utilities/hashtable.inline.hpp" #if INCLUDE_G1GC @@ -82,8 +83,8 @@ bool MetaspaceShared::_remapped_readwrite = false; address MetaspaceShared::_i2i_entry_code_buffers = NULL; size_t MetaspaceShared::_i2i_entry_code_buffers_size = 0; -size_t MetaspaceShared::_core_spaces_size = 0; void* MetaspaceShared::_shared_metaspace_static_top = NULL; +intx MetaspaceShared::_relocation_delta; // The CDS archive is divided into the following regions: // mc - misc code (the method entry trampolines) @@ -147,9 +148,21 @@ return p; } +void DumpRegion::append_intptr_t(intptr_t n, bool need_to_mark) { + assert(is_aligned(_top, sizeof(intptr_t)), "bad alignment"); + intptr_t *p = (intptr_t*)_top; + char* newtop = _top + sizeof(intptr_t); + expand_top_to(newtop); + *p = n; + if (need_to_mark) { + ArchivePtrMarker::mark_pointer(p); + } +} + void DumpRegion::print(size_t total_bytes) const { tty->print_cr("%-3s space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [%5.1f%% used] at " INTPTR_FORMAT, - _name, used(), percent_of(used(), total_bytes), reserved(), percent_of(used(), reserved()), p2i(_base)); + _name, used(), percent_of(used(), total_bytes), reserved(), percent_of(used(), reserved()), + p2i(_base + MetaspaceShared::final_delta())); } void DumpRegion::print_out_of_space_msg(const char* failing_region, size_t needed_bytes) { @@ -172,14 +185,14 @@ } } -DumpRegion _mc_region("mc"), _ro_region("ro"), _rw_region("rw"), _md_region("md"); -size_t _total_closed_archive_region_size = 0, _total_open_archive_region_size = 0; +static DumpRegion _mc_region("mc"), _ro_region("ro"), _rw_region("rw"), _md_region("md"); +static size_t _total_closed_archive_region_size = 0, _total_open_archive_region_size = 0; void MetaspaceShared::init_shared_dump_space(DumpRegion* first_space, address first_space_bottom) { // Start with 0 committed bytes. The memory will be committed as needed by // MetaspaceShared::commit_shared_space_to(). if (!_shared_vs.initialize(_shared_rs, 0)) { - vm_exit_during_initialization("Unable to allocate memory for shared space"); + fatal("Unable to allocate memory for shared space"); } first_space->init(&_shared_rs, (char*)first_space_bottom); } @@ -209,73 +222,32 @@ return _ro_region.allocate(num_bytes); } -void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() { - assert(UseSharedSpaces, "Must be called when UseSharedSpaces is enabled"); - - // If using shared space, open the file that contains the shared space - // and map in the memory before initializing the rest of metaspace (so - // the addresses don't conflict) - FileMapInfo* mapinfo = new FileMapInfo(true); - - // Open the shared archive file, read and validate the header. If - // initialization fails, shared spaces [UseSharedSpaces] are - // disabled and the file is closed. - // Map in spaces now also - if (mapinfo->initialize(true) && map_shared_spaces(mapinfo)) { - size_t cds_total = core_spaces_size(); - address cds_address = (address)mapinfo->region_addr(0); - char* cds_end = (char *)align_up(cds_address + cds_total, - Metaspace::reserve_alignment()); - - // Mapping the dynamic archive before allocating the class space - cds_end = initialize_dynamic_runtime_shared_spaces((char*)cds_address, cds_end); - -#ifdef _LP64 - if (Metaspace::using_class_space()) { - // If UseCompressedClassPointers is set then allocate the metaspace area - // above the heap and above the CDS area (if it exists). - Metaspace::allocate_metaspace_compressed_klass_ptrs(cds_end, cds_address); - // map_heap_regions() compares the current narrow oop and klass encodings - // with the archived ones, so it must be done after all encodings are determined. - mapinfo->map_heap_regions(); - } - CompressedKlassPointers::set_range(CompressedClassSpaceSize); -#endif // _LP64 +// When reserving an address range using ReservedSpace, we need an alignment that satisfies both: +// os::vm_allocation_granularity() -- so that we can sub-divide this range into multiple mmap regions, +// while keeping the first range at offset 0 of this range. +// Metaspace::reserve_alignment() -- so we can pass the region to +// Metaspace::allocate_metaspace_compressed_klass_ptrs. +size_t MetaspaceShared::reserved_space_alignment() { + size_t os_align = os::vm_allocation_granularity(); + size_t ms_align = Metaspace::reserve_alignment(); + if (os_align >= ms_align) { + assert(os_align % ms_align == 0, "must be a multiple"); + return os_align; } else { - assert(!mapinfo->is_open() && !UseSharedSpaces, - "archive file not closed or shared spaces not disabled."); + assert(ms_align % os_align == 0, "must be a multiple"); + return ms_align; } } -char* MetaspaceShared::initialize_dynamic_runtime_shared_spaces( - char* static_start, char* static_end) { - assert(UseSharedSpaces, "must be runtime"); - char* cds_end = static_end; - if (!DynamicDumpSharedSpaces) { - address dynamic_top = DynamicArchive::map(); - if (dynamic_top != NULL) { - assert(dynamic_top > (address)static_start, "Unexpected layout"); - MetaspaceObj::expand_shared_metaspace_range(dynamic_top); - cds_end = (char *)align_up(dynamic_top, Metaspace::reserve_alignment()); - } - } - return cds_end; -} - -ReservedSpace* MetaspaceShared::reserve_shared_rs(size_t size, size_t alignment, - bool large, char* requested_address) { - if (requested_address != NULL) { - _shared_rs = ReservedSpace(size, alignment, large, requested_address); - } else { - _shared_rs = ReservedSpace(size, alignment, large); - } - return &_shared_rs; +ReservedSpace MetaspaceShared::reserve_shared_space(size_t size, char* requested_address) { + bool large_pages = false; // Don't use large pages for the CDS archive. + assert(is_aligned(requested_address, reserved_space_alignment()), "must be"); + return ReservedSpace(size, reserved_space_alignment(), large_pages, requested_address); } void MetaspaceShared::initialize_dumptime_shared_and_meta_spaces() { assert(DumpSharedSpaces, "should be called for dump time only"); - const size_t reserve_alignment = Metaspace::reserve_alignment(); - bool large_pages = false; // No large pages when dumping the CDS archive. + const size_t reserve_alignment = reserved_space_alignment(); char* shared_base = (char*)align_up((char*)SharedBaseAddress, reserve_alignment); #ifdef _LP64 @@ -296,15 +268,22 @@ size_t cds_total = align_down(256*M, reserve_alignment); #endif + bool use_requested_base = true; + if (ArchiveRelocationMode == 1) { + log_info(cds)("ArchiveRelocationMode == 1: always allocate class space at an alternative address"); + use_requested_base = false; + } + // First try to reserve the space at the specified SharedBaseAddress. - //_shared_rs = ReservedSpace(cds_total, reserve_alignment, large_pages, shared_base); - reserve_shared_rs(cds_total, reserve_alignment, large_pages, shared_base); + assert(!_shared_rs.is_reserved(), "must be"); + if (use_requested_base) { + _shared_rs = reserve_shared_space(cds_total, shared_base); + } if (_shared_rs.is_reserved()) { assert(shared_base == 0 || _shared_rs.base() == shared_base, "should match"); } else { // Get a mmap region anywhere if the SharedBaseAddress fails. - //_shared_rs = ReservedSpace(cds_total, reserve_alignment, large_pages); - reserve_shared_rs(cds_total, reserve_alignment, large_pages, NULL); + _shared_rs = reserve_shared_space(cds_total); } if (!_shared_rs.is_reserved()) { vm_exit_during_initialization("Unable to reserve memory for shared space", @@ -442,6 +421,8 @@ assert(commit <= uncommitted, "sanity"); bool result = _shared_vs.expand_by(commit, false); + ArchivePtrMarker::expand_ptr_end((address*)_shared_vs.high()); + if (!result) { vm_exit_during_initialization(err_msg("Failed to expand shared space to " SIZE_FORMAT " bytes", need_committed_size)); @@ -451,6 +432,10 @@ commit, _shared_vs.actual_committed_size(), _shared_vs.high()); } +void MetaspaceShared::initialize_ptr_marker(CHeapBitMap* ptrmap) { + ArchivePtrMarker::initialize(ptrmap, (address*)_shared_vs.low(), (address*)_shared_vs.high()); +} + // Read/write a data stream for restoring/preserving metadata pointers and // miscellaneous data from/to the shared archive file. @@ -469,6 +454,7 @@ soc->do_tag(sizeof(Symbol)); // Dump/restore miscellaneous metadata. + JavaClasses::serialize_offsets(soc); Universe::serialize(soc); soc->do_tag(--tag); @@ -482,7 +468,6 @@ HeapShared::serialize_subgraph_info_table_header(soc); SystemDictionaryShared::serialize_dictionary_headers(soc); - JavaClasses::serialize_offsets(soc); InstanceMirrorKlass::serialize_offsets(soc); soc->do_tag(--tag); @@ -705,7 +690,9 @@ // Switch the vtable pointer to point to the cloned vtable. static void patch(Metadata* obj) { assert(DumpSharedSpaces, "dump-time only"); + assert(MetaspaceShared::is_in_output_space(obj), "must be"); *(void**)obj = (void*)(_info->cloned_vtable()); + ArchivePtrMarker::mark_pointer(obj); } static bool is_valid_shared_object(const T* obj) { @@ -799,7 +786,8 @@ } #define ALLOC_CPP_VTABLE_CLONE(c) \ - _cloned_cpp_vtptrs[c##_Kind] = CppVtableCloner::allocate(#c); + _cloned_cpp_vtptrs[c##_Kind] = CppVtableCloner::allocate(#c); \ + ArchivePtrMarker::mark_pointer(&_cloned_cpp_vtptrs[c##_Kind]); #define CLONE_CPP_VTABLE(c) \ p = CppVtableCloner::clone_vtable(#c, (CppVtableInfo*)p); @@ -965,7 +953,7 @@ assert(size % sizeof(intptr_t) == 0, "bad size"); do_tag((int)size); while (size > 0) { - _dump_region->append_intptr_t(*(intptr_t*)start); + _dump_region->append_intptr_t(*(intptr_t*)start, true); start += sizeof(intptr_t); size -= sizeof(intptr_t); } @@ -1129,9 +1117,13 @@ GrowableArray* oopmaps); void dump_symbols(); char* dump_read_only_tables(); + void print_class_stats(); void print_region_stats(); + void print_bitmap_region_stats(size_t size, size_t total_size); void print_heap_region_stats(GrowableArray *heap_mem, - const char *name, const size_t total_size); + const char *name, size_t total_size); + void relocate_to_default_base_address(CHeapBitMap* ptrmap); + public: VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; } @@ -1276,6 +1268,15 @@ ref->metaspace_pointers_do_at(&refer, new_loc); return true; // recurse into ref.obj() } + virtual void push_special(SpecialRef type, Ref* ref, intptr_t* p) { + assert(type == _method_entry_ref, "only special type allowed for now"); + address obj = ref->obj(); + address new_obj = get_new_loc(ref); + size_t offset = pointer_delta(p, obj, sizeof(u1)); + intptr_t* new_p = (intptr_t*)(new_obj + offset); + assert(*p == *new_p, "must be a copy"); + ArchivePtrMarker::mark_pointer((address*)new_p); + } }; // Relocate a reference to point to its shallow copy @@ -1284,6 +1285,7 @@ virtual bool do_ref(Ref* ref, bool read_only) { if (ref->not_null()) { ref->update(get_new_loc(ref)); + ArchivePtrMarker::mark_pointer(ref->addr()); } return false; // Do not recurse. } @@ -1440,7 +1442,71 @@ return start; } +void VM_PopulateDumpSharedSpace::print_class_stats() { + tty->print_cr("Number of classes %d", _global_klass_objects->length()); + { + int num_type_array = 0, num_obj_array = 0, num_inst = 0; + for (int i = 0; i < _global_klass_objects->length(); i++) { + Klass* k = _global_klass_objects->at(i); + if (k->is_instance_klass()) { + num_inst ++; + } else if (k->is_objArray_klass()) { + num_obj_array ++; + } else { + assert(k->is_typeArray_klass(), "sanity"); + num_type_array ++; + } + } + tty->print_cr(" instance classes = %5d", num_inst); + tty->print_cr(" obj array classes = %5d", num_obj_array); + tty->print_cr(" type array classes = %5d", num_type_array); + } +} + +void VM_PopulateDumpSharedSpace::relocate_to_default_base_address(CHeapBitMap* ptrmap) { + intx addr_delta = MetaspaceShared::final_delta(); + if (addr_delta == 0) { + ArchivePtrMarker::compact((address)SharedBaseAddress, (address)_md_region.top()); + } else { + // We are not able to reserve space at Arguments::default_SharedBaseAddress() (due to ASLR). + // This means that the current content of the archive is based on a random + // address. Let's relocate all the pointers, so that it can be mapped to + // Arguments::default_SharedBaseAddress() without runtime relocation. + // + // Note: both the base and dynamic archive are written with + // FileMapHeader::_shared_base_address == Arguments::default_SharedBaseAddress() + + // Patch all pointers that are marked by ptrmap within this region, + // where we have just dumped all the metaspace data. + address patch_base = (address)SharedBaseAddress; + address patch_end = (address)_md_region.top(); + size_t size = patch_end - patch_base; + + // the current value of the pointers to be patched must be within this + // range (i.e., must point to valid metaspace objects) + address valid_old_base = patch_base; + address valid_old_end = patch_end; + + // after patching, the pointers must point inside this range + // (the requested location of the archive, as mapped at runtime). + address valid_new_base = (address)Arguments::default_SharedBaseAddress(); + address valid_new_end = valid_new_base + size; + + log_debug(cds)("Relocating archive from [" INTPTR_FORMAT " - " INTPTR_FORMAT " ] to " + "[" INTPTR_FORMAT " - " INTPTR_FORMAT " ]", p2i(patch_base), p2i(patch_end), + p2i(valid_new_base), p2i(valid_new_end)); + + SharedDataRelocator patcher((address*)patch_base, (address*)patch_end, valid_old_base, valid_old_end, + valid_new_base, valid_new_end, addr_delta, ptrmap); + ptrmap->iterate(&patcher); + ArchivePtrMarker::compact(patcher.max_non_null_offset()); + } +} + void VM_PopulateDumpSharedSpace::doit() { + CHeapBitMap ptrmap; + MetaspaceShared::initialize_ptr_marker(&ptrmap); + // We should no longer allocate anything from the metaspace, so that: // // (1) Metaspace::allocate might trigger GC if we have run out of @@ -1472,24 +1538,7 @@ CollectClassesClosure collect_classes; ClassLoaderDataGraph::loaded_classes_do(&collect_classes); - tty->print_cr("Number of classes %d", _global_klass_objects->length()); - { - int num_type_array = 0, num_obj_array = 0, num_inst = 0; - for (int i = 0; i < _global_klass_objects->length(); i++) { - Klass* k = _global_klass_objects->at(i); - if (k->is_instance_klass()) { - num_inst ++; - } else if (k->is_objArray_klass()) { - num_obj_array ++; - } else { - assert(k->is_typeArray_klass(), "sanity"); - num_type_array ++; - } - } - tty->print_cr(" instance classes = %5d", num_inst); - tty->print_cr(" obj array classes = %5d", num_obj_array); - tty->print_cr(" type array classes = %5d", num_type_array); - } + print_class_stats(); // Ensure the ConstMethods won't be modified at run-time tty->print("Updating ConstMethods ... "); @@ -1520,12 +1569,6 @@ MetaspaceShared::allocate_cpp_vtable_clones(); _md_region.pack(); - // The 4 core spaces are allocated consecutively mc->rw->ro->md, so there total size - // is just the spaces between the two ends. - size_t core_spaces_size = _md_region.end() - _mc_region.base(); - assert(core_spaces_size == (size_t)align_up(core_spaces_size, Metaspace::reserve_alignment()), - "should already be aligned"); - // During patching, some virtual methods may be called, so at this point // the vtables must contain valid methods (as filled in by CppVtableCloner::allocate). MetaspaceShared::patch_cpp_vtable_pointers(); @@ -1534,6 +1577,10 @@ // We don't want to write these addresses into the archive. MetaspaceShared::zero_cpp_vtable_clones_for_writing(); + // relocate the data so that it can be mapped to Arguments::default_SharedBaseAddress() + // without runtime relocation. + relocate_to_default_base_address(&ptrmap); + // Create and write the archive file that maps the shared spaces. FileMapInfo* mapinfo = new FileMapInfo(true); @@ -1542,7 +1589,6 @@ mapinfo->set_misc_data_patching_start(vtbl_list); mapinfo->set_i2i_entry_code_buffers(MetaspaceShared::i2i_entry_code_buffers(), MetaspaceShared::i2i_entry_code_buffers_size()); - mapinfo->set_core_spaces_size(core_spaces_size); mapinfo->open_for_write(); // NOTE: md contains the trampoline code for method entries, which are patched at run time, @@ -1552,6 +1598,8 @@ write_region(mapinfo, MetaspaceShared::ro, &_ro_region, /*read_only=*/true, /*allow_exec=*/false); write_region(mapinfo, MetaspaceShared::md, &_md_region, /*read_only=*/false,/*allow_exec=*/false); + mapinfo->write_bitmap_region(ArchivePtrMarker::ptrmap()); + _total_closed_archive_region_size = mapinfo->write_archive_heap_regions( _closed_archive_heap_regions, _closed_archive_heap_oopmaps, @@ -1563,6 +1611,7 @@ MetaspaceShared::first_open_archive_heap_region, MetaspaceShared::max_open_archive_heap_region); + mapinfo->set_final_requested_base((char*)Arguments::default_SharedBaseAddress()); mapinfo->set_header_crc(mapinfo->compute_header_crc()); mapinfo->write_header(); mapinfo->close(); @@ -1594,12 +1643,16 @@ void VM_PopulateDumpSharedSpace::print_region_stats() { // Print statistics of all the regions + const size_t bitmap_used = ArchivePtrMarker::ptrmap()->size_in_bytes(); + const size_t bitmap_reserved = align_up(bitmap_used, Metaspace::reserve_alignment()); const size_t total_reserved = _ro_region.reserved() + _rw_region.reserved() + _mc_region.reserved() + _md_region.reserved() + + bitmap_reserved + _total_closed_archive_region_size + _total_open_archive_region_size; const size_t total_bytes = _ro_region.used() + _rw_region.used() + _mc_region.used() + _md_region.used() + + bitmap_used + _total_closed_archive_region_size + _total_open_archive_region_size; const double total_u_perc = percent_of(total_bytes, total_reserved); @@ -1608,6 +1661,7 @@ _rw_region.print(total_reserved); _ro_region.print(total_reserved); _md_region.print(total_reserved); + print_bitmap_region_stats(bitmap_reserved, total_reserved); print_heap_region_stats(_closed_archive_heap_regions, "ca", total_reserved); print_heap_region_stats(_open_archive_heap_regions, "oa", total_reserved); @@ -1615,8 +1669,13 @@ total_bytes, total_reserved, total_u_perc); } +void VM_PopulateDumpSharedSpace::print_bitmap_region_stats(size_t size, size_t total_size) { + tty->print_cr("bm space: " SIZE_FORMAT_W(9) " [ %4.1f%% of total] out of " SIZE_FORMAT_W(9) " bytes [100.0%% used] at " INTPTR_FORMAT, + size, size/double(total_size)*100.0, size, p2i(NULL)); +} + void VM_PopulateDumpSharedSpace::print_heap_region_stats(GrowableArray *heap_mem, - const char *name, const size_t total_size) { + const char *name, size_t total_size) { int arr_len = heap_mem == NULL ? 0 : heap_mem->length(); for (int i = 0; i < arr_len; i++) { char* start = (char*)heap_mem->at(i).start(); @@ -1636,9 +1695,13 @@ o->set_klass(k); } -Klass* MetaspaceShared::get_relocated_klass(Klass *k) { +Klass* MetaspaceShared::get_relocated_klass(Klass *k, bool is_final) { assert(DumpSharedSpaces, "sanity"); - return ArchiveCompactor::get_relocated_klass(k); + k = ArchiveCompactor::get_relocated_klass(k); + if (is_final) { + k = (Klass*)(address(k) + final_delta()); + } + return k; } class LinkSharedClassesClosure : public KlassClosure { @@ -1947,8 +2010,9 @@ } } -void MetaspaceShared::set_shared_metaspace_range(void* base, void* top) { - _shared_metaspace_static_top = top; +void MetaspaceShared::set_shared_metaspace_range(void* base, void *static_top, void* top) { + assert(base <= static_top && static_top <= top, "must be"); + _shared_metaspace_static_top = static_top; MetaspaceObj::set_shared_metaspace_range(base, top); } @@ -1973,49 +2037,312 @@ } } -// Map shared spaces at requested addresses and return if succeeded. -bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) { - size_t image_alignment = mapinfo->alignment(); +void MetaspaceShared::initialize_runtime_shared_and_meta_spaces() { + assert(UseSharedSpaces, "Must be called when UseSharedSpaces is enabled"); + MapArchiveResult result = MAP_ARCHIVE_OTHER_FAILURE; + FileMapInfo* static_mapinfo = open_static_archive(); + FileMapInfo* dynamic_mapinfo = NULL; + + if (static_mapinfo != NULL) { + dynamic_mapinfo = open_dynamic_archive(); + + // First try to map at the requested address + result = map_archives(static_mapinfo, dynamic_mapinfo, true); + if (result == MAP_ARCHIVE_MMAP_FAILURE) { + // Mapping has failed (probably due to ASLR). Let's map at an address chosen + // by the OS. + result = map_archives(static_mapinfo, dynamic_mapinfo, false); + } + } + + if (result == MAP_ARCHIVE_SUCCESS) { + bool dynamic_mapped = (dynamic_mapinfo != NULL && dynamic_mapinfo->is_mapped()); + char* cds_base = static_mapinfo->mapped_base(); + char* cds_end = dynamic_mapped ? dynamic_mapinfo->mapped_end() : static_mapinfo->mapped_end(); + set_shared_metaspace_range(cds_base, static_mapinfo->mapped_end(), cds_end); + _relocation_delta = static_mapinfo->relocation_delta(); + if (dynamic_mapped) { + FileMapInfo::set_shared_path_table(dynamic_mapinfo); + } else { + FileMapInfo::set_shared_path_table(static_mapinfo); + } + } else { + set_shared_metaspace_range(NULL, NULL, NULL); + UseSharedSpaces = false; + FileMapInfo::fail_continue("Unable to map shared spaces"); + if (PrintSharedArchiveAndExit) { + vm_exit_during_initialization("Unable to use shared archive."); + } + } + + if (static_mapinfo != NULL && !static_mapinfo->is_mapped()) { + delete static_mapinfo; + } + if (dynamic_mapinfo != NULL && !dynamic_mapinfo->is_mapped()) { + delete dynamic_mapinfo; + } +} + +FileMapInfo* MetaspaceShared::open_static_archive() { + FileMapInfo* mapinfo = new FileMapInfo(true); + if (!mapinfo->initialize()) { + delete(mapinfo); + return NULL; + } + return mapinfo; +} + +FileMapInfo* MetaspaceShared::open_dynamic_archive() { + if (DynamicDumpSharedSpaces) { + return NULL; + } + if (Arguments::GetSharedDynamicArchivePath() == NULL) { + return NULL; + } -#ifndef _WINDOWS - // Map in the shared memory and then map the regions on top of it. - // On Windows, don't map the memory here because it will cause the - // mappings of the regions to fail. - ReservedSpace shared_rs = mapinfo->reserve_shared_memory(); - if (!shared_rs.is_reserved()) return false; -#endif + FileMapInfo* mapinfo = new FileMapInfo(false); + if (!mapinfo->initialize()) { + delete(mapinfo); + return NULL; + } + return mapinfo; +} + +// use_requested_addr: +// true = map at FileMapHeader::_requested_base_address +// false = map at an alternative address picked by OS. +MapArchiveResult MetaspaceShared::map_archives(FileMapInfo* static_mapinfo, FileMapInfo* dynamic_mapinfo, + bool use_requested_addr) { + PRODUCT_ONLY(if (ArchiveRelocationMode == 1 && use_requested_addr) { + // For product build only -- this is for benchmarking the cost of doing relocation. + // For debug builds, the check is done in FileMapInfo::map_regions for better test coverage. + log_info(cds)("ArchiveRelocationMode == 1: always map archive(s) at an alternative address"); + return MAP_ARCHIVE_MMAP_FAILURE; + }); + + if (ArchiveRelocationMode == 2 && !use_requested_addr) { + log_info(cds)("ArchiveRelocationMode == 2: never map archive(s) at an alternative address"); + return MAP_ARCHIVE_MMAP_FAILURE; + }; + + if (dynamic_mapinfo != NULL) { + // Ensure that the OS won't be able to allocate new memory spaces between the two + // archives, or else it would mess up the simple comparision in MetaspaceObj::is_shared(). + assert(static_mapinfo->mapping_end_offset() == dynamic_mapinfo->mapping_base_offset(), "no gap"); + } - assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces"); + ReservedSpace main_rs, archive_space_rs, class_space_rs; + MapArchiveResult result = MAP_ARCHIVE_OTHER_FAILURE; + char* mapped_base_address = reserve_address_space_for_archives(static_mapinfo, dynamic_mapinfo, + use_requested_addr, main_rs, archive_space_rs, + class_space_rs); + if (mapped_base_address == NULL) { + result = MAP_ARCHIVE_MMAP_FAILURE; + } else { + log_debug(cds)("Reserved archive_space_rs [" INTPTR_FORMAT " - " INTPTR_FORMAT "] (" SIZE_FORMAT ") bytes", + p2i(archive_space_rs.base()), p2i(archive_space_rs.end()), archive_space_rs.size()); + log_debug(cds)("Reserved class_space_rs [" INTPTR_FORMAT " - " INTPTR_FORMAT "] (" SIZE_FORMAT ") bytes", + p2i(class_space_rs.base()), p2i(class_space_rs.end()), class_space_rs.size()); + MapArchiveResult static_result = map_archive(static_mapinfo, mapped_base_address, archive_space_rs); + MapArchiveResult dynamic_result = (static_result == MAP_ARCHIVE_SUCCESS) ? + map_archive(dynamic_mapinfo, mapped_base_address, archive_space_rs) : MAP_ARCHIVE_OTHER_FAILURE; - // Map each shared region - int regions[] = {mc, rw, ro, md}; - size_t len = sizeof(regions)/sizeof(int); - char* saved_base[] = {NULL, NULL, NULL, NULL}; - char* top = mapinfo->map_regions(regions, saved_base, len ); + if (static_result == MAP_ARCHIVE_SUCCESS) { + if (dynamic_result == MAP_ARCHIVE_SUCCESS) { + result = MAP_ARCHIVE_SUCCESS; + } else if (dynamic_result == MAP_ARCHIVE_OTHER_FAILURE) { + assert(dynamic_mapinfo != NULL && !dynamic_mapinfo->is_mapped(), "must have failed"); + // No need to retry mapping the dynamic archive again, as it will never succeed + // (bad file, etc) -- just keep the base archive. + log_warning(cds, dynamic)("Unable to use shared archive. The top archive failed to load: %s", + dynamic_mapinfo->full_path()); + result = MAP_ARCHIVE_SUCCESS; + // TODO, we can give the unused space for the dynamic archive to class_space_rs, but there's no + // easy API to do that right now. + } else { + result = MAP_ARCHIVE_MMAP_FAILURE; + } + } else if (static_result == MAP_ARCHIVE_OTHER_FAILURE) { + result = MAP_ARCHIVE_OTHER_FAILURE; + } else { + result = MAP_ARCHIVE_MMAP_FAILURE; + } + } - if (top != NULL && - (image_alignment == (size_t)os::vm_allocation_granularity()) && - mapinfo->validate_shared_path_table()) { - // Success -- set up MetaspaceObj::_shared_metaspace_{base,top} for - // fast checking in MetaspaceShared::is_in_shared_metaspace() and - // MetaspaceObj::is_shared(). - _core_spaces_size = mapinfo->core_spaces_size(); - set_shared_metaspace_range((void*)saved_base[0], (void*)top); - return true; + if (result == MAP_ARCHIVE_SUCCESS) { + if (!main_rs.is_reserved() && class_space_rs.is_reserved()) { + MemTracker::record_virtual_memory_type((address)class_space_rs.base(), mtClass); + } + SharedBaseAddress = (size_t)mapped_base_address; + LP64_ONLY({ + if (Metaspace::using_class_space()) { + assert(class_space_rs.is_reserved(), "must be"); + char* cds_base = static_mapinfo->mapped_base(); + Metaspace::allocate_metaspace_compressed_klass_ptrs(class_space_rs, NULL, (address)cds_base); + // map_heap_regions() compares the current narrow oop and klass encodings + // with the archived ones, so it must be done after all encodings are determined. + static_mapinfo->map_heap_regions(); + } + CompressedKlassPointers::set_range(CompressedClassSpaceSize); + }); + } else { + unmap_archive(static_mapinfo); + unmap_archive(dynamic_mapinfo); + release_reserved_spaces(main_rs, archive_space_rs, class_space_rs); + } + + return result; +} + +char* MetaspaceShared::reserve_address_space_for_archives(FileMapInfo* static_mapinfo, + FileMapInfo* dynamic_mapinfo, + bool use_requested_addr, + ReservedSpace& main_rs, + ReservedSpace& archive_space_rs, + ReservedSpace& class_space_rs) { + const bool use_klass_space = NOT_LP64(false) LP64_ONLY(Metaspace::using_class_space()); + const size_t class_space_size = NOT_LP64(0) LP64_ONLY(Metaspace::compressed_class_space_size()); + + if (use_klass_space) { + assert(class_space_size > 0, "CompressedClassSpaceSize must have been validated"); + } + if (use_requested_addr && !is_aligned(static_mapinfo->requested_base_address(), reserved_space_alignment())) { + return NULL; + } + + // Size and requested location of the archive_space_rs (for both static and dynamic archives) + size_t base_offset = static_mapinfo->mapping_base_offset(); + size_t end_offset = (dynamic_mapinfo == NULL) ? static_mapinfo->mapping_end_offset() : dynamic_mapinfo->mapping_end_offset(); + assert(base_offset == 0, "must be"); + assert(is_aligned(end_offset, os::vm_allocation_granularity()), "must be"); + assert(is_aligned(base_offset, os::vm_allocation_granularity()), "must be"); + + // In case reserved_space_alignment() != os::vm_allocation_granularity() + assert((size_t)os::vm_allocation_granularity() <= reserved_space_alignment(), "must be"); + end_offset = align_up(end_offset, reserved_space_alignment()); + + size_t archive_space_size = end_offset - base_offset; + + // Special handling for Windows because it cannot mmap into a reserved space: + // use_requested_addr: We just map each region individually, and give up if any one of them fails. + // !use_requested_addr: We reserve the space first, and then os::read in all the regions (instead of mmap). + // We're going to patch all the pointers anyway so there's no benefit for mmap. + + if (use_requested_addr) { + char* archive_space_base = static_mapinfo->requested_base_address() + base_offset; + char* archive_space_end = archive_space_base + archive_space_size; + if (!MetaspaceShared::use_windows_memory_mapping()) { + archive_space_rs = reserve_shared_space(archive_space_size, archive_space_base); + if (!archive_space_rs.is_reserved()) { + return NULL; + } + } + if (use_klass_space) { + // Make sure we can map the klass space immediately following the archive_space space + char* class_space_base = archive_space_end; + class_space_rs = reserve_shared_space(class_space_size, class_space_base); + if (!class_space_rs.is_reserved()) { + return NULL; + } + } + return static_mapinfo->requested_base_address(); } else { - mapinfo->unmap_regions(regions, saved_base, len); -#ifndef _WINDOWS - // Release the entire mapped region - shared_rs.release(); -#endif - // If -Xshare:on is specified, print out the error message and exit VM, - // otherwise, set UseSharedSpaces to false and continue. - if (RequireSharedSpaces || PrintSharedArchiveAndExit) { - vm_exit_during_initialization("Unable to use shared archive.", "Failed map_region for using -Xshare:on."); + if (use_klass_space) { + main_rs = reserve_shared_space(archive_space_size + class_space_size); + if (main_rs.is_reserved()) { + archive_space_rs = main_rs.first_part(archive_space_size, reserved_space_alignment(), /*split=*/true); + class_space_rs = main_rs.last_part(archive_space_size); + } + } else { + main_rs = reserve_shared_space(archive_space_size); + archive_space_rs = main_rs; + } + if (archive_space_rs.is_reserved()) { + return archive_space_rs.base(); } else { - FLAG_SET_DEFAULT(UseSharedSpaces, false); + return NULL; + } + } +} + +void MetaspaceShared::release_reserved_spaces(ReservedSpace& main_rs, + ReservedSpace& archive_space_rs, + ReservedSpace& class_space_rs) { + if (main_rs.is_reserved()) { + assert(main_rs.contains(archive_space_rs.base()), "must be"); + assert(main_rs.contains(class_space_rs.base()), "must be"); + log_debug(cds)("Released shared space (archive+classes) " INTPTR_FORMAT, p2i(main_rs.base())); + main_rs.release(); + } else { + if (archive_space_rs.is_reserved()) { + log_debug(cds)("Released shared space (archive) " INTPTR_FORMAT, p2i(archive_space_rs.base())); + archive_space_rs.release(); + } + if (class_space_rs.is_reserved()) { + log_debug(cds)("Released shared space (classes) " INTPTR_FORMAT, p2i(class_space_rs.base())); + class_space_rs.release(); } - return false; + } +} + +static int static_regions[] = {MetaspaceShared::mc, + MetaspaceShared::rw, + MetaspaceShared::ro, + MetaspaceShared::md}; +static int dynamic_regions[] = {MetaspaceShared::rw, + MetaspaceShared::ro, + MetaspaceShared::mc}; +static int static_regions_count = 4; +static int dynamic_regions_count = 3; + +MapArchiveResult MetaspaceShared::map_archive(FileMapInfo* mapinfo, char* mapped_base_address, ReservedSpace rs) { + assert(UseSharedSpaces, "must be runtime"); + if (mapinfo == NULL) { + return MAP_ARCHIVE_SUCCESS; // no error has happeed -- trivially succeeded. + } + + mapinfo->set_is_mapped(false); + + if (mapinfo->alignment() != (size_t)os::vm_allocation_granularity()) { + log_error(cds)("Unable to map CDS archive -- os::vm_allocation_granularity() expected: " SIZE_FORMAT + " actual: %d", mapinfo->alignment(), os::vm_allocation_granularity()); + return MAP_ARCHIVE_OTHER_FAILURE; + } + + MapArchiveResult result = mapinfo->is_static() ? + mapinfo->map_regions(static_regions, static_regions_count, mapped_base_address, rs) : + mapinfo->map_regions(dynamic_regions, dynamic_regions_count, mapped_base_address, rs); + + if (result != MAP_ARCHIVE_SUCCESS) { + unmap_archive(mapinfo); + return result; + } + + if (mapinfo->is_static()) { + if (!mapinfo->validate_shared_path_table()) { + unmap_archive(mapinfo); + return MAP_ARCHIVE_OTHER_FAILURE; + } + } else { + if (!DynamicArchive::validate(mapinfo)) { + unmap_archive(mapinfo); + return MAP_ARCHIVE_OTHER_FAILURE; + } + } + + mapinfo->set_is_mapped(true); + return MAP_ARCHIVE_SUCCESS; +} + +void MetaspaceShared::unmap_archive(FileMapInfo* mapinfo) { + assert(UseSharedSpaces, "must be runtime"); + if (mapinfo != NULL) { + if (mapinfo->is_static()) { + mapinfo->unmap_regions(static_regions, static_regions_count); + } else { + mapinfo->unmap_regions(dynamic_regions, dynamic_regions_count); + } + mapinfo->set_is_mapped(false); } } @@ -2023,17 +2350,15 @@ // serialize it out to its various destinations. void MetaspaceShared::initialize_shared_spaces() { - FileMapInfo *mapinfo = FileMapInfo::current_info(); - _i2i_entry_code_buffers = mapinfo->i2i_entry_code_buffers(); - _i2i_entry_code_buffers_size = mapinfo->i2i_entry_code_buffers_size(); - // _core_spaces_size is loaded from the shared archive immediatelly after mapping - assert(_core_spaces_size == mapinfo->core_spaces_size(), "sanity"); - char* buffer = mapinfo->misc_data_patching_start(); + FileMapInfo *static_mapinfo = FileMapInfo::current_info(); + _i2i_entry_code_buffers = static_mapinfo->i2i_entry_code_buffers(); + _i2i_entry_code_buffers_size = static_mapinfo->i2i_entry_code_buffers_size(); + char* buffer = static_mapinfo->misc_data_patching_start(); clone_cpp_vtables((intptr_t*)buffer); // Verify various attributes of the archive, plus initialize the // shared string/symbol tables - buffer = mapinfo->serialized_data_start(); + buffer = static_mapinfo->serialized_data_start(); intptr_t* array = (intptr_t*)buffer; ReadClosure rc(&array); serialize(&rc); @@ -2041,17 +2366,26 @@ // Initialize the run-time symbol table. SymbolTable::create_table(); - mapinfo->patch_archived_heap_embedded_pointers(); + static_mapinfo->patch_archived_heap_embedded_pointers(); // Close the mapinfo file - mapinfo->close(); + static_mapinfo->close(); + + FileMapInfo *dynamic_mapinfo = FileMapInfo::dynamic_info(); + if (dynamic_mapinfo != NULL) { + intptr_t* buffer = (intptr_t*)dynamic_mapinfo->serialized_data_start(); + ReadClosure rc(&buffer); + SymbolTable::serialize_shared_table_header(&rc, false); + SystemDictionaryShared::serialize_dictionary_headers(&rc, false); + dynamic_mapinfo->close(); + } if (PrintSharedArchiveAndExit) { if (PrintSharedDictionary) { tty->print_cr("\nShared classes:\n"); SystemDictionaryShared::print_on(tty); } - if (_archive_loading_failed) { + if (FileMapInfo::current_info() == NULL || _archive_loading_failed) { tty->print_cr("archive is invalid"); vm_exit(1); } else { @@ -2094,3 +2428,10 @@ vm_exit_during_initialization(err_msg("Unable to allocate from '%s' region", name), "Please reduce the number of shared classes."); } + +// This is used to relocate the pointers so that the archive can be mapped at +// Arguments::default_SharedBaseAddress() without runtime relocation. +intx MetaspaceShared::final_delta() { + return intx(Arguments::default_SharedBaseAddress()) // We want the archive to be mapped to here at runtime + - intx(SharedBaseAddress); // .. but the archive is mapped at here at dump time +} diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/metaspaceShared.hpp --- a/src/hotspot/share/memory/metaspaceShared.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/metaspaceShared.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -37,6 +37,13 @@ #define MAX_SHARED_DELTA (0x7FFFFFFF) class FileMapInfo; +class CHeapBitMap; + +enum MapArchiveResult { + MAP_ARCHIVE_SUCCESS, + MAP_ARCHIVE_MMAP_FAILURE, + MAP_ARCHIVE_OTHER_FAILURE +}; class MetaspaceSharedStats { public: @@ -62,13 +69,7 @@ char* expand_top_to(char* newtop); char* allocate(size_t num_bytes, size_t alignment=BytesPerWord); - void append_intptr_t(intptr_t n) { - assert(is_aligned(_top, sizeof(intptr_t)), "bad alignment"); - intptr_t *p = (intptr_t*)_top; - char* newtop = _top + sizeof(intptr_t); - expand_top_to(newtop); - *p = n; - } + void append_intptr_t(intptr_t n, bool need_to_mark = false); char* base() const { return _base; } char* top() const { return _top; } @@ -117,17 +118,15 @@ } void do_ptr(void** p) { - _dump_region->append_intptr_t((intptr_t)*p); + _dump_region->append_intptr_t((intptr_t)*p, true); } void do_u4(u4* p) { - void* ptr = (void*)(uintx(*p)); - do_ptr(&ptr); + _dump_region->append_intptr_t((intptr_t)(*p)); } void do_bool(bool *p) { - void* ptr = (void*)(uintx(*p)); - do_ptr(&ptr); + _dump_region->append_intptr_t((intptr_t)(*p)); } void do_tag(int tag) { @@ -170,7 +169,7 @@ bool reading() const { return true; } }; -#endif +#endif // INCLUDE_CDS // Class Data Sharing Support class MetaspaceShared : AllStatic { @@ -187,6 +186,7 @@ static size_t _i2i_entry_code_buffers_size; static size_t _core_spaces_size; static void* _shared_metaspace_static_top; + static intx _relocation_delta; public: enum { // core archive spaces @@ -194,11 +194,12 @@ rw = 1, // read-write shared space in the heap ro = 2, // read-only shared space in the heap md = 3, // miscellaneous data for initializing tables, etc. - num_core_spaces = 4, // number of non-string regions - num_non_heap_spaces = 4, + bm = 4, // relocation bitmaps (freed after file mapping is finished) + num_core_region = 4, + num_non_heap_spaces = 5, // mapped java heap regions - first_closed_archive_heap_region = md + 1, + first_closed_archive_heap_region = bm + 1, max_closed_archive_heap_region = 2, last_closed_archive_heap_region = first_closed_archive_heap_region + max_closed_archive_heap_region - 1, first_open_archive_heap_region = last_closed_archive_heap_region + 1, @@ -220,16 +221,14 @@ CDS_ONLY(return &_shared_rs); NOT_CDS(return NULL); } + + static void set_shared_rs(ReservedSpace rs) { + CDS_ONLY(_shared_rs = rs); + } + static void commit_shared_space_to(char* newtop) NOT_CDS_RETURN; - static size_t core_spaces_size() { - assert(DumpSharedSpaces || UseSharedSpaces, "sanity"); - assert(_core_spaces_size != 0, "sanity"); - return _core_spaces_size; - } static void initialize_dumptime_shared_and_meta_spaces() NOT_CDS_RETURN; static void initialize_runtime_shared_and_meta_spaces() NOT_CDS_RETURN; - static char* initialize_dynamic_runtime_shared_spaces( - char* static_start, char* static_end) NOT_CDS_RETURN_(NULL); static void post_initialize(TRAPS) NOT_CDS_RETURN; // Delta of this object from SharedBaseAddress @@ -245,22 +244,25 @@ static void set_archive_loading_failed() { _archive_loading_failed = true; } + static bool is_in_output_space(void* ptr) { + assert(DumpSharedSpaces, "must be"); + return shared_rs()->contains(ptr); + } + static bool map_shared_spaces(FileMapInfo* mapinfo) NOT_CDS_RETURN_(false); static void initialize_shared_spaces() NOT_CDS_RETURN; // Return true if given address is in the shared metaspace regions (i.e., excluding any // mapped shared heap regions.) static bool is_in_shared_metaspace(const void* p) { - // If no shared metaspace regions are mapped, MetaspceObj::_shared_metaspace_{base,top} will - // both be NULL and all values of p will be rejected quickly. - return (p < MetaspaceObj::shared_metaspace_top() && p >= MetaspaceObj::shared_metaspace_base()); + return MetaspaceObj::is_shared((const MetaspaceObj*)p); } static address shared_metaspace_top() { return (address)MetaspaceObj::shared_metaspace_top(); } - static void set_shared_metaspace_range(void* base, void* top) NOT_CDS_RETURN; + static void set_shared_metaspace_range(void* base, void *static_top, void* top) NOT_CDS_RETURN; // Return true if given address is in the shared region corresponding to the idx static bool is_in_shared_region(const void* p, int idx) NOT_CDS_RETURN_(false); @@ -298,8 +300,8 @@ static void link_and_cleanup_shared_classes(TRAPS); #if INCLUDE_CDS - static ReservedSpace* reserve_shared_rs(size_t size, size_t alignment, - bool large, char* requested_address); + static ReservedSpace reserve_shared_space(size_t size, char* requested_address = NULL); + static size_t reserved_space_alignment(); static void init_shared_dump_space(DumpRegion* first_space, address first_space_bottom = NULL); static DumpRegion* misc_code_dump_space(); static DumpRegion* read_write_dump_space(); @@ -342,11 +344,35 @@ } static void relocate_klass_ptr(oop o); - static Klass* get_relocated_klass(Klass *k); + static Klass* get_relocated_klass(Klass *k, bool is_final=false); static intptr_t* fix_cpp_vtable_for_dynamic_archive(MetaspaceObj::Type msotype, address obj); + static void initialize_ptr_marker(CHeapBitMap* ptrmap); + // Non-zero if the archive(s) need to be mapped a non-default location due to ASLR. + static intx relocation_delta() { return _relocation_delta; } + static intx final_delta(); + static bool use_windows_memory_mapping() { + const bool is_windows = (NOT_WINDOWS(false) WINDOWS_ONLY(true)); + //const bool is_windows = true; // enable this to allow testing the windows mmap semantics on Linux, etc. + return is_windows; + } private: static void read_extra_data(const char* filename, TRAPS) NOT_CDS_RETURN; + static FileMapInfo* open_static_archive(); + static FileMapInfo* open_dynamic_archive(); + static MapArchiveResult map_archives(FileMapInfo* static_mapinfo, FileMapInfo* dynamic_mapinfo, + bool use_requested_addr); + static char* reserve_address_space_for_archives(FileMapInfo* static_mapinfo, + FileMapInfo* dynamic_mapinfo, + bool use_requested_addr, + ReservedSpace& main_rs, + ReservedSpace& archive_space_rs, + ReservedSpace& class_space_rs); + static void release_reserved_spaces(ReservedSpace& main_rs, + ReservedSpace& archive_space_rs, + ReservedSpace& class_space_rs); + static MapArchiveResult map_archive(FileMapInfo* mapinfo, char* mapped_base_address, ReservedSpace rs); + static void unmap_archive(FileMapInfo* mapinfo); }; #endif // SHARE_MEMORY_METASPACESHARED_HPP diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/memory/universe.cpp --- a/src/hotspot/share/memory/universe.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/memory/universe.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -85,18 +85,24 @@ #include "utilities/ostream.hpp" #include "utilities/preserveException.hpp" +#define PRIMITIVE_MIRRORS_DO(func) \ + func(_int_mirror) \ + func(_float_mirror) \ + func(_double_mirror) \ + func(_byte_mirror) \ + func(_bool_mirror) \ + func(_char_mirror) \ + func(_long_mirror) \ + func(_short_mirror) \ + func(_void_mirror) + +#define DEFINE_PRIMITIVE_MIRROR(m) \ + oop Universe::m = NULL; + // Known objects +PRIMITIVE_MIRRORS_DO(DEFINE_PRIMITIVE_MIRROR) Klass* Universe::_typeArrayKlassObjs[T_LONG+1] = { NULL /*, NULL...*/ }; Klass* Universe::_objectArrayKlassObj = NULL; -oop Universe::_int_mirror = NULL; -oop Universe::_float_mirror = NULL; -oop Universe::_double_mirror = NULL; -oop Universe::_byte_mirror = NULL; -oop Universe::_bool_mirror = NULL; -oop Universe::_char_mirror = NULL; -oop Universe::_long_mirror = NULL; -oop Universe::_short_mirror = NULL; -oop Universe::_void_mirror = NULL; oop Universe::_mirrors[T_VOID+1] = { NULL /*, NULL...*/ }; oop Universe::_main_thread_group = NULL; oop Universe::_system_thread_group = NULL; @@ -167,17 +173,11 @@ } } -void Universe::oops_do(OopClosure* f) { +#define DO_PRIMITIVE_MIRROR(m) \ + f->do_oop((oop*) &m); - f->do_oop((oop*) &_int_mirror); - f->do_oop((oop*) &_float_mirror); - f->do_oop((oop*) &_double_mirror); - f->do_oop((oop*) &_byte_mirror); - f->do_oop((oop*) &_bool_mirror); - f->do_oop((oop*) &_char_mirror); - f->do_oop((oop*) &_long_mirror); - f->do_oop((oop*) &_short_mirror); - f->do_oop((oop*) &_void_mirror); +void Universe::oops_do(OopClosure* f) { + PRIMITIVE_MIRRORS_DO(DO_PRIMITIVE_MIRROR); for (int i = T_BOOLEAN; i < T_VOID+1; i++) { f->do_oop((oop*) &_mirrors[i]); @@ -231,6 +231,13 @@ _do_stack_walk_cache->metaspace_pointers_do(it); } +#define ASSERT_MIRROR_NULL(m) \ + assert(m == NULL, "archived mirrors should be NULL"); + +#define SERIALIZE_MIRROR(m) \ + f->do_oop(&m); \ + if (m != NULL) { java_lang_Class::update_archived_primitive_mirror_native_pointers(m); } + // Serialize metadata and pointers to primitive type mirrors in and out of CDS archive void Universe::serialize(SerializeClosure* f) { @@ -239,25 +246,12 @@ } f->do_ptr((void**)&_objectArrayKlassObj); + #if INCLUDE_CDS_JAVA_HEAP -#ifdef ASSERT - if (DumpSharedSpaces && !HeapShared::is_heap_object_archiving_allowed()) { - assert(_int_mirror == NULL && _float_mirror == NULL && - _double_mirror == NULL && _byte_mirror == NULL && - _bool_mirror == NULL && _char_mirror == NULL && - _long_mirror == NULL && _short_mirror == NULL && - _void_mirror == NULL, "mirrors should be NULL"); - } -#endif - f->do_oop(&_int_mirror); - f->do_oop(&_float_mirror); - f->do_oop(&_double_mirror); - f->do_oop(&_byte_mirror); - f->do_oop(&_bool_mirror); - f->do_oop(&_char_mirror); - f->do_oop(&_long_mirror); - f->do_oop(&_short_mirror); - f->do_oop(&_void_mirror); + DEBUG_ONLY(if (DumpSharedSpaces && !HeapShared::is_heap_object_archiving_allowed()) { + PRIMITIVE_MIRRORS_DO(ASSERT_MIRROR_NULL); + }); + PRIMITIVE_MIRRORS_DO(SERIALIZE_MIRROR); #endif f->do_ptr((void**)&_the_array_interfaces_array); @@ -419,18 +413,18 @@ #endif } +#define ASSERT_MIRROR_NOT_NULL(m) \ + assert(m != NULL, "archived mirrors should not be NULL"); + void Universe::initialize_basic_type_mirrors(TRAPS) { #if INCLUDE_CDS_JAVA_HEAP if (UseSharedSpaces && HeapShared::open_archive_heap_region_mapped() && _int_mirror != NULL) { assert(HeapShared::is_heap_object_archiving_allowed(), "Sanity"); - assert(_float_mirror != NULL && _double_mirror != NULL && - _byte_mirror != NULL && _byte_mirror != NULL && - _bool_mirror != NULL && _char_mirror != NULL && - _long_mirror != NULL && _short_mirror != NULL && - _void_mirror != NULL, "Sanity"); + PRIMITIVE_MIRRORS_DO(ASSERT_MIRROR_NOT_NULL); } else + // _int_mirror could be NULL if archived heap is not mapped. #endif { _int_mirror = diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/oops/constMethod.cpp --- a/src/hotspot/share/oops/constMethod.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/oops/constMethod.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -420,6 +420,8 @@ if (has_default_annotations()) { it->push(default_annotations_addr()); } + ConstMethod* this_ptr = this; + it->push_method_entry(&this_ptr, (intptr_t*)&_adapter_trampoline); } // Printing diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/oops/instanceKlass.cpp --- a/src/hotspot/share/oops/instanceKlass.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/oops/instanceKlass.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -1577,11 +1577,30 @@ } #endif -static int binary_search(const Array* methods, const Symbol* name) { +bool InstanceKlass::_disable_method_binary_search = false; + +int InstanceKlass::quick_search(const Array* methods, const Symbol* name) { int len = methods->length(); - // methods are sorted, so do binary search int l = 0; int h = len - 1; + + if (_disable_method_binary_search) { + // At the final stage of dynamic dumping, the methods array may not be sorted + // by ascending addresses of their names, so we can't use binary search anymore. + // However, methods with the same name are still laid out consecutively inside the + // methods array, so let's look for the first one that matches. + assert(DynamicDumpSharedSpaces, "must be"); + while (l <= h) { + Method* m = methods->at(l); + if (m->name() == name) { + return l; + } + l ++; + } + return -1; + } + + // methods are sorted by ascending addresses of their names, so do binary search while (l <= h) { int mid = (l + h) >> 1; Method* m = methods->at(mid); @@ -1733,7 +1752,7 @@ const bool skipping_overpass = (overpass_mode == skip_overpass); const bool skipping_static = (static_mode == skip_static); const bool skipping_private = (private_mode == skip_private); - const int hit = binary_search(methods, name); + const int hit = quick_search(methods, name); if (hit != -1) { const Method* const m = methods->at(hit); @@ -1784,7 +1803,7 @@ const Symbol* name, int* end_ptr) { assert(end_ptr != NULL, "just checking"); - int start = binary_search(methods, name); + int start = quick_search(methods, name); int end = start + 1; if (start != -1) { while (start - 1 >= 0 && (methods->at(start - 1))->name() == name) --start; @@ -2365,6 +2384,7 @@ _breakpoints = NULL; _previous_versions = NULL; _cached_class_file = NULL; + _jvmti_cached_class_field_map = NULL; #endif _init_thread = NULL; @@ -2373,6 +2393,8 @@ _oop_map_cache = NULL; // clear _nest_host to ensure re-load at runtime _nest_host = NULL; + _package_entry = NULL; + _dep_context_last_cleaned = 0; } void InstanceKlass::remove_java_mirror() { diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/oops/instanceKlass.hpp --- a/src/hotspot/share/oops/instanceKlass.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/oops/instanceKlass.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -329,6 +329,8 @@ friend class SystemDictionary; + static bool _disable_method_binary_search; + public: u2 loader_type() { return _misc_flags & loader_type_bits(); @@ -564,6 +566,14 @@ bool find_local_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const; bool find_field_from_offset(int offset, bool is_static, fieldDescriptor* fd) const; + private: + static int quick_search(const Array* methods, const Symbol* name); + + public: + static void disable_method_binary_search() { + _disable_method_binary_search = true; + } + // find a local method (returns NULL if not found) Method* find_method(const Symbol* name, const Symbol* signature) const; static Method* find_method(const Array* methods, diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/oops/method.cpp --- a/src/hotspot/share/oops/method.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/oops/method.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -332,18 +332,21 @@ return align_metadata_size(header_size() + extra_words); } - Symbol* Method::klass_name() const { return method_holder()->name(); } - void Method::metaspace_pointers_do(MetaspaceClosure* it) { log_trace(cds)("Iter(Method): %p", this); it->push(&_constMethod); it->push(&_method_data); it->push(&_method_counters); + + Method* this_ptr = this; + it->push_method_entry(&this_ptr, (intptr_t*)&_i2i_entry); + it->push_method_entry(&this_ptr, (intptr_t*)&_from_compiled_entry); + it->push_method_entry(&this_ptr, (intptr_t*)&_from_interpreted_entry); } // Attempt to return method oop to original state. Clear any pointers @@ -1741,12 +1744,15 @@ // This is only done during class loading, so it is OK to assume method_idnum matches the methods() array // default_methods also uses this without the ordering for fast find_method -void Method::sort_methods(Array* methods, bool set_idnums) { +void Method::sort_methods(Array* methods, bool set_idnums, method_comparator_func func) { int length = methods->length(); if (length > 1) { + if (func == NULL) { + func = method_comparator; + } { NoSafepointVerifier nsv; - QuickSort::sort(methods->data(), length, method_comparator, /*idempotent=*/false); + QuickSort::sort(methods->data(), length, func, /*idempotent=*/false); } // Reset method ordering if (set_idnums) { diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/oops/method.hpp --- a/src/hotspot/share/oops/method.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/oops/method.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -1006,8 +1006,10 @@ void print_name(outputStream* st = tty) PRODUCT_RETURN; // prints as "virtual void foo(int)" #endif + typedef int (*method_comparator_func)(Method* a, Method* b); + // Helper routine used for method sorting - static void sort_methods(Array* methods, bool set_idnums = true); + static void sort_methods(Array* methods, bool set_idnums = true, method_comparator_func func = NULL); // Deallocation function for redefine classes or if an error occurs void deallocate_contents(ClassLoaderData* loader_data); diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/runtime/arguments.cpp --- a/src/hotspot/share/runtime/arguments.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/runtime/arguments.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -85,6 +85,7 @@ bool Arguments::_ClipInlining = ClipInlining; intx Arguments::_Tier3InvokeNotifyFreqLog = Tier3InvokeNotifyFreqLog; intx Arguments::_Tier4InvocationThreshold = Tier4InvocationThreshold; +size_t Arguments::_SharedBaseAddress = SharedBaseAddress; bool Arguments::_enable_preview = false; @@ -2274,6 +2275,9 @@ Arguments::_Tier4InvocationThreshold = Tier4InvocationThreshold; } + // CDS dumping always write the archive to the default value of SharedBaseAddress. + Arguments::_SharedBaseAddress = SharedBaseAddress; + // Setup flags for mixed which is the default set_mode_flags(_mixed); diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/runtime/arguments.hpp --- a/src/hotspot/share/runtime/arguments.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/runtime/arguments.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -481,6 +481,7 @@ static char* SharedArchivePath; static char* SharedDynamicArchivePath; + static size_t _SharedBaseAddress; // The default value specified in globals.hpp static int num_archives(const char* archive_path) NOT_CDS_RETURN_(0); static void extract_shared_archive_paths(const char* archive_path, char** base_archive_path, @@ -563,7 +564,7 @@ static const char* GetSharedArchivePath() { return SharedArchivePath; } static const char* GetSharedDynamicArchivePath() { return SharedDynamicArchivePath; } - + static size_t default_SharedBaseAddress() { return _SharedBaseAddress; } // Java launcher properties static void process_sun_java_launcher_properties(JavaVMInitArgs* args); diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/runtime/globals.hpp --- a/src/hotspot/share/runtime/globals.hpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/runtime/globals.hpp Wed Nov 13 16:36:54 2019 -0800 @@ -2430,6 +2430,14 @@ product(ccstr, ExtraSharedClassListFile, NULL, \ "Extra classlist for building the CDS archive file") \ \ + diagnostic(intx, ArchiveRelocationMode, 0, \ + "(0) first map at preferred address, and if " \ + "unsuccessful, map at alternative address (default); " \ + "(1) always map at alternative address; " \ + "(2) always map at preferred address, and if unsuccessful, " \ + "do not map the archive") \ + range(0, 2) \ + \ experimental(size_t, ArrayAllocatorMallocLimit, \ SOLARIS_ONLY(64*K) NOT_SOLARIS((size_t)-1), \ "Allocation less than this value will be allocated " \ diff -r e0d59f0c2b7d -r 22ee476cc664 src/hotspot/share/runtime/vmStructs.cpp --- a/src/hotspot/share/runtime/vmStructs.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/hotspot/share/runtime/vmStructs.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -1103,7 +1103,7 @@ CDS_ONLY(nonstatic_field(FileMapInfo, _header, FileMapHeader*)) \ CDS_ONLY( static_field(FileMapInfo, _current_info, FileMapInfo*)) \ CDS_ONLY(nonstatic_field(FileMapHeader, _space[0], CDSFileMapRegion)) \ - CDS_ONLY(nonstatic_field(CDSFileMapRegion, _addr._base, char*)) \ + CDS_ONLY(nonstatic_field(CDSFileMapRegion, _mapped_base, char*)) \ CDS_ONLY(nonstatic_field(CDSFileMapRegion, _used, size_t)) \ \ /******************/ \ diff -r e0d59f0c2b7d -r 22ee476cc664 src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/FileMapInfo.java --- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/FileMapInfo.java Thu Nov 14 10:02:52 2019 +0800 +++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/memory/FileMapInfo.java Wed Nov 13 16:36:54 2019 -0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2018, 2019, 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 @@ -70,7 +70,7 @@ // SpaceInfo type = db.lookupType("CDSFileMapRegion"); - long mdRegionBaseAddressOffset = type.getField("_addr._base").getOffset(); + long mdRegionBaseAddressOffset = type.getField("_mapped_base").getOffset(); mdRegionBaseAddress = (mdSpaceValue.addOffsetTo(mdRegionBaseAddressOffset)).getAddressAt(0); long mdRegionSizeOffset = type.getField("_used").getOffset(); long mdRegionSize = (mdSpaceValue.addOffsetTo(mdRegionSizeOffset)).getAddressAt(0).asLongValue(); diff -r e0d59f0c2b7d -r 22ee476cc664 src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c --- a/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c Thu Nov 14 10:02:52 2019 +0800 +++ b/src/jdk.hotspot.agent/share/native/libsaproc/ps_core_common.c Wed Nov 13 16:36:54 2019 -0800 @@ -261,6 +261,7 @@ // mangled name of Arguments::SharedArchivePath #define SHARED_ARCHIVE_PATH_SYM "_ZN9Arguments17SharedArchivePathE" #define USE_SHARED_SPACES_SYM "UseSharedSpaces" +#define SHARED_BASE_ADDRESS_SYM "SharedBaseAddress" #define LIBJVM_NAME "/libjvm.so" #endif @@ -268,6 +269,7 @@ // mangled name of Arguments::SharedArchivePath #define SHARED_ARCHIVE_PATH_SYM "__ZN9Arguments17SharedArchivePathE" #define USE_SHARED_SPACES_SYM "_UseSharedSpaces" +#define SHARED_BASE_ADDRESS_SYM "_SharedBaseAddress" #define LIBJVM_NAME "/libjvm.dylib" #endif @@ -281,7 +283,8 @@ char classes_jsa[PATH_MAX]; CDSFileMapHeaderBase header; int fd = -1; - uintptr_t base = 0, useSharedSpacesAddr = 0; + uintptr_t useSharedSpacesAddr = 0; + uintptr_t sharedBaseAddressAddr = 0, sharedBaseAddress = 0; uintptr_t sharedArchivePathAddrAddr = 0, sharedArchivePathAddr = 0; jboolean useSharedSpaces = 0; int m; @@ -308,6 +311,17 @@ return true; } + sharedBaseAddressAddr = lookup_symbol(ph, jvm_name, SHARED_BASE_ADDRESS_SYM); + if (sharedBaseAddressAddr == 0) { + print_debug("can't lookup 'SharedBaseAddress' flag\n"); + return false; + } + + if (read_pointer(ph, sharedBaseAddressAddr, &sharedBaseAddress) != true) { + print_debug("can't read the value of 'SharedBaseAddress' flag\n"); + return false; + } + sharedArchivePathAddrAddr = lookup_symbol(ph, jvm_name, SHARED_ARCHIVE_PATH_SYM); if (sharedArchivePathAddrAddr == 0) { print_debug("can't lookup shared archive path symbol\n"); @@ -363,16 +377,19 @@ ph->core->classes_jsa_fd = fd; // add read-only maps from classes.jsa to the list of maps for (m = 0; m < NUM_CDS_REGIONS; m++) { - if (header._space[m]._read_only) { + if (header._space[m]._read_only && + !header._space[m]._is_heap_region && + !header._space[m]._is_bitmap_region) { // With *some* linux versions, the core file doesn't include read-only mmap'ed // files regions, so let's add them here. This is harmless if the core file also // include these regions. - base = (uintptr_t) header._space[m]._addr._base; + uintptr_t base = sharedBaseAddress + (uintptr_t) header._space[m]._mapping_offset; + size_t size = header._space[m]._used; // no need to worry about the fractional pages at-the-end. // possible fractional pages are handled by core_read_data. add_class_share_map_info(ph, (off_t) header._space[m]._file_offset, - base, (size_t) header._space[m]._used); - print_debug("added a share archive map at 0x%lx\n", base); + base, size); + print_debug("added a share archive map [%d] at 0x%lx (size 0x%lx bytes)\n", m, base, size); } } return true; diff -r e0d59f0c2b7d -r 22ee476cc664 src/jdk.hotspot.agent/solaris/native/libsaproc/saproc.cpp --- a/src/jdk.hotspot.agent/solaris/native/libsaproc/saproc.cpp Thu Nov 14 10:02:52 2019 +0800 +++ b/src/jdk.hotspot.agent/solaris/native/libsaproc/saproc.cpp Wed Nov 13 16:36:54 2019 -0800 @@ -538,9 +538,11 @@ } #define USE_SHARED_SPACES_SYM "UseSharedSpaces" +#define SHARED_BASE_ADDRESS_SYM "SharedBaseAddress" // mangled symbol name for Arguments::SharedArchivePath #define SHARED_ARCHIVE_PATH_SYM "__1cJArgumentsRSharedArchivePath_" +static uintptr_t sharedBaseAddress = 0; static int init_classsharing_workaround(void *cd, const prmap_t* pmap, const char* obj_name) { Debugger* dbg = (Debugger*) cd; @@ -577,6 +579,19 @@ return 1; } + psaddr_t sharedBaseAddressAddr = 0; + ps_pglobal_lookup(ph, jvm_name, SHARED_ARCHIVE_PATH_SYM, &sharedBaseAddressAddr); + if (sharedBaseAddressAddr == 0) { + print_debug("can't find symbol 'SharedBaseAddress'\n"); + THROW_NEW_DEBUGGER_EXCEPTION_("can't find 'SharedBaseAddress' flag\n", 1); + } + + sharedBaseAddress = 0; + if (read_pointer(ph, sharedBaseAddressAddr, &sharedBaseAddress) != true) { + print_debug("can't read the value of 'SharedBaseAddress' flag\n"); + THROW_NEW_DEBUGGER_EXCEPTION_("can't get SharedBaseAddress from debuggee", 1); + } + char classes_jsa[PATH_MAX]; psaddr_t sharedArchivePathAddrAddr = 0; ps_pglobal_lookup(ph, jvm_name, SHARED_ARCHIVE_PATH_SYM, &sharedArchivePathAddrAddr); @@ -648,9 +663,14 @@ if (_libsaproc_debug) { for (int m = 0; m < NUM_CDS_REGIONS; m++) { - print_debug("shared file offset %d mapped at 0x%lx, size = %ld, read only? = %d\n", - pheader->_space[m]._file_offset, pheader->_space[m]._addr._base, - pheader->_space[m]._used, pheader->_space[m]._read_only); + if (!pheader->_space[m]._is_heap_region && + !pheader->_space[m]._is_bitmap_region) { + jlong mapping_offset = pheader->_space[m]._mapping_offset; + jlong baseAddress = mapping_offset + (jlong)sharedBaseAddress; + print_debug("shared file offset %d mapped at 0x%lx, size = %ld, read only? = %d\n", + pheader->_space[m]._file_offset, baseAddress, + pheader->_space[m]._used, pheader->_space[m]._read_only); + } } } @@ -1052,11 +1072,14 @@ // We can skip the non-read-only maps. These are mapped as MAP_PRIVATE // and hence will be read by libproc. Besides, the file copy may be // stale because the process might have modified those pages. - if (pheader->_space[m]._read_only) { - jlong baseAddress = (jlong) (uintptr_t) pheader->_space[m]._addr._base; - size_t usedSize = pheader->_space[m]._used; - if (address >= baseAddress && address < (baseAddress + usedSize)) { - // the given address falls in this shared heap area + if (pheader->_space[m]._read_only && + !pheader->_space[m]._is_heap_region && + !pheader->_space[m]._is_bitmap_region) { + jlong mapping_offset = (jlong) (uintptr_t) pheader->_space[m]._mapping_offset; + jlong baseAddress = mapping_offset + (jlong)sharedBaseAddress; + size_t usedSize = pheader->_space[m]._used; + if (address >= baseAddress && address < (baseAddress + usedSize)) { + // the given address falls in this shared metadata area print_debug("found shared map at 0x%lx\n", (long) baseAddress); diff -r e0d59f0c2b7d -r 22ee476cc664 test/hotspot/jtreg/TEST.groups --- a/test/hotspot/jtreg/TEST.groups Thu Nov 14 10:02:52 2019 +0800 +++ b/test/hotspot/jtreg/TEST.groups Wed Nov 13 16:36:54 2019 -0800 @@ -322,6 +322,7 @@ -runtime/cds/appcds/javaldr/GCSharedStringsDuringDump.java \ -runtime/cds/appcds/javaldr/HumongousDuringDump.java \ -runtime/cds/appcds/sharedStrings \ + -runtime/cds/appcds/ArchiveRelocationTest.java \ -runtime/cds/appcds/DumpClassList.java \ -runtime/cds/appcds/ExtraSymbols.java \ -runtime/cds/appcds/LongClassListPath.java \ @@ -332,6 +333,15 @@ -runtime/cds/appcds/UnusedCPDuringDump.java \ -runtime/cds/appcds/VerifierTest_1B.java +hotspot_cds_relocation = \ + gc/g1/TestSharedArchiveWithPreTouch.java \ + runtime/cds \ + runtime/modules/ModulesSymLink.java \ + runtime/modules/PatchModule/PatchModuleCDS.java \ + runtime/modules/PatchModule/PatchModuleClassList.java \ + runtime/NMT \ + serviceability/sa + # A subset of AppCDS tests to be run in tier1 tier1_runtime_appcds = \ runtime/cds/appcds/HelloTest.java \ diff -r e0d59f0c2b7d -r 22ee476cc664 test/hotspot/jtreg/runtime/cds/SpaceUtilizationCheck.java --- a/test/hotspot/jtreg/runtime/cds/SpaceUtilizationCheck.java Thu Nov 14 10:02:52 2019 +0800 +++ b/test/hotspot/jtreg/runtime/cds/SpaceUtilizationCheck.java Wed Nov 13 16:36:54 2019 -0800 @@ -73,8 +73,8 @@ Matcher matcher = pattern.matcher(line); if (matcher.find()) { String name = matcher.group(1); - if (name.equals("s0") || name.equals("s1")) { - // String regions are listed at the end and they may not be fully occupied. + if (name.equals("bm")) { + // Bitmap space does not have a requested address. break; } else { System.out.println("Checking " + name + " in : " + line); diff -r e0d59f0c2b7d -r 22ee476cc664 test/hotspot/jtreg/runtime/cds/appcds/ArchiveRelocationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/runtime/cds/appcds/ArchiveRelocationTest.java Wed Nov 13 16:36:54 2019 -0800 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019, 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 + * @comment the test uses -XX:ArchiveRelocationMode=1 to force relocation. + * @requires vm.cds + * @summary Testing relocation of CDS archive (during both dump time and run time) + * @comment JDK-8231610 Relocate the CDS archive if it cannot be mapped to the requested address + * @bug 8231610 + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @build Hello + * @run driver ClassFileInstaller -jar hello.jar Hello + * @run driver ArchiveRelocationTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jtreg.SkippedException; + +public class ArchiveRelocationTest { + public static void main(String... args) throws Exception { + try { + test(true, false); + test(false, true); + test(true, true); + } catch (SkippedException s) { + s.printStackTrace(); + throw new RuntimeException("Archive mapping should always succeed after JDK-8231610 (did the machine run out of memory?)"); + } + } + + static int caseCount = 0; + + // dump_reloc - force relocation of archive during dump time? + // run_reloc - force relocation of archive during run time? + static void test(boolean dump_reloc, boolean run_reloc) throws Exception { + caseCount += 1; + System.out.println("============================================================"); + System.out.println("case = " + caseCount + ", dump = " + dump_reloc + + ", run = " + run_reloc); + System.out.println("============================================================"); + + + String appJar = ClassFileInstaller.getJarPath("hello.jar"); + String mainClass = "Hello"; + String forceRelocation = "-XX:ArchiveRelocationMode=1"; + String dumpRelocArg = dump_reloc ? forceRelocation : "-showversion"; + String runRelocArg = run_reloc ? forceRelocation : "-showversion"; + String logArg = "-Xlog:cds=debug,cds+reloc=debug"; + String unlockArg = "-XX:+UnlockDiagnosticVMOptions"; + + OutputAnalyzer out = TestCommon.dump(appJar, + TestCommon.list(mainClass), + unlockArg, dumpRelocArg, logArg); + if (dump_reloc) { + out.shouldContain("ArchiveRelocationMode == 1: always allocate class space at an alternative address"); + out.shouldContain("Relocating archive from"); + } + + TestCommon.run("-cp", appJar, unlockArg, runRelocArg, logArg, mainClass) + .assertNormalExit(output -> { + if (run_reloc) { + output.shouldContain("ArchiveRelocationMode == 1: always map archive(s) at an alternative address"); + output.shouldContain("runtime archive relocation start"); + output.shouldContain("runtime archive relocation done"); + } + }); + } +} diff -r e0d59f0c2b7d -r 22ee476cc664 test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveRelocationTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveRelocationTest.java Wed Nov 13 16:36:54 2019 -0800 @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2019, 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 + * @comment the test uses -XX:ArchiveRelocationMode=1 to force relocation. + * @requires vm.cds + * @summary Testing relocation of dynamic CDS archive (during both dump time and run time) + * @comment JDK-8231610 Relocate the CDS archive if it cannot be mapped to the requested address + * @bug 8231610 + * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds /test/hotspot/jtreg/runtime/cds/appcds/test-classes + * @build Hello + * @run driver ClassFileInstaller -jar hello.jar Hello + * @run driver DynamicArchiveRelocationTest + */ + +import jdk.test.lib.process.OutputAnalyzer; +import jtreg.SkippedException; + +public class DynamicArchiveRelocationTest extends DynamicArchiveTestBase { + public static void main(String... args) throws Exception { + try { + testOuter(false); + testOuter(true); + } catch (SkippedException s) { + s.printStackTrace(); + throw new RuntimeException("Archive mapping should always succeed after JDK-8231610 (did the machine run out of memory?)"); + } + } + + static void testOuter(boolean dump_base_reloc) throws Exception { + testInner(dump_base_reloc, true, false); + testInner(dump_base_reloc, false, true); + testInner(dump_base_reloc, true, true); + } + + static boolean dump_base_reloc, dump_top_reloc, run_reloc; + + // dump_base_reloc - force relocation of archive when dumping base archive + // dump_top_reloc - force relocation of archive when dumping top archive + // run_reloc - force relocation of archive when running + static void testInner(boolean dump_base_reloc, boolean dump_top_reloc, boolean run_reloc) throws Exception { + DynamicArchiveRelocationTest.dump_base_reloc = dump_base_reloc; + DynamicArchiveRelocationTest.dump_top_reloc = dump_top_reloc; + DynamicArchiveRelocationTest.run_reloc = run_reloc; + + runTest(DynamicArchiveRelocationTest::doTest); + } + + static int caseCount = 0; + static void doTest() throws Exception { + caseCount += 1; + System.out.println("============================================================"); + System.out.println("case = " + caseCount + ", base = " + dump_base_reloc + + ", top = " + dump_top_reloc + + ", run = " + run_reloc); + System.out.println("============================================================"); + + String appJar = ClassFileInstaller.getJarPath("hello.jar"); + String mainClass = "Hello"; + String forceRelocation = "-XX:ArchiveRelocationMode=1"; + String dumpBaseRelocArg = dump_base_reloc ? forceRelocation : "-showversion"; + String dumpTopRelocArg = dump_top_reloc ? forceRelocation : "-showversion"; + String runRelocArg = run_reloc ? forceRelocation : "-showversion"; + String logArg = "-Xlog:cds=debug,cds+reloc=debug"; + + String baseArchiveName = getNewArchiveName("base"); + String topArchiveName = getNewArchiveName("top"); + + String runtimeMsg1 = "ArchiveRelocationMode == 1: always map archive(s) at an alternative address"; + String runtimeMsg2 = "runtime archive relocation start"; + String runtimeMsg3 = "runtime archive relocation done"; + String unlockArg = "-XX:+UnlockDiagnosticVMOptions"; + + // (1) Dump base archive (static) + + OutputAnalyzer out = dumpBaseArchive(baseArchiveName, unlockArg, dumpBaseRelocArg, logArg); + if (dump_base_reloc) { + out.shouldContain("ArchiveRelocationMode == 1: always allocate class space at an alternative address"); + out.shouldContain("Relocating archive from"); + } + + // (2) Dump top archive (dynamic) + + dump2(baseArchiveName, topArchiveName, + unlockArg, + dumpTopRelocArg, + logArg, + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + if (dump_top_reloc) { + output.shouldContain(runtimeMsg1); + output.shouldContain(runtimeMsg2); + output.shouldContain(runtimeMsg3); + } + }); + + run2(baseArchiveName, topArchiveName, + unlockArg, + runRelocArg, + logArg, + "-cp", appJar, mainClass) + .assertNormalExit(output -> { + if (run_reloc) { + output.shouldContain(runtimeMsg1); + output.shouldContain(runtimeMsg2); + output.shouldContain(runtimeMsg3); + } + }); + } +} diff -r e0d59f0c2b7d -r 22ee476cc664 test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveTestBase.java --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveTestBase.java Thu Nov 14 10:02:52 2019 +0800 +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/DynamicArchiveTestBase.java Wed Nov 13 16:36:54 2019 -0800 @@ -134,7 +134,7 @@ * Dump the base archive. The JDK's default class list is used (unless otherwise specified * in cmdLineSuffix). */ - public static void dumpBaseArchive(String baseArchiveName, String ... cmdLineSuffix) + public static OutputAnalyzer dumpBaseArchive(String baseArchiveName, String ... cmdLineSuffix) throws Exception { CDSOptions opts = new CDSOptions(); @@ -143,6 +143,7 @@ opts.addSuffix("-Djava.class.path="); OutputAnalyzer out = CDSTestUtils.createArchive(opts); CDSTestUtils.checkDump(out); + return out; } /**