8231610: Relocate the CDS archive if it cannot be mapped to the requested address
Reviewed-by: jiangli, coleenp, ccheung
--- 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
--- 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
--- 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<Symbol*>(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<Symbol*>(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
--- 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 <typename T>
+ static unsigned int hash_for_shared_dictionary(T* ptr) {
+ assert(ptr > (T*)SharedBaseAddress, "must be");
+ address p = address(ptr) - SharedBaseAddress;
+ return primitive_hash<address>(p);
+ }
};
#endif // SHARE_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
--- 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 {
--- 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; }
--- /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
--- /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 <typename T>
+ 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 <bool COMPACTING>
+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
--- 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<address>(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<u8>* 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<u8>* 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<true> 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;
-}
--- 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
--- 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<const char*>* 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<false> 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; i<num_ranges; i++) {
FileMapRegion* si = space_at(i + first_region_idx);
- HeapShared::patch_archived_heap_embedded_pointers(ranges[i], (address)si->oopmap(),
+ 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
--- 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<u8>* _table;
int _size;
public:
+ SharedPathTable() : _table(NULL), _size(0) {}
+ SharedPathTable(Array<u8>* 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<u8>* 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<u8>*)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<MemRegion> *heap_mem,
GrowableArray<ArchiveHeapOopmapInfo> *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<const char*>* 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<const char*>* 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);
--- 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*>(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<Klass*>(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);
--- 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;
--- 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
--- 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 <class T> void push(T** mpp, Writability w = _default) {
push_impl(new ObjectRef<T>(mpp, w));
}
+
+ template <class T> void push_method_entry(T** mpp, intptr_t* p) {
+ push_special(_method_entry_ref, new ObjectRef<T>(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.
--- 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<c>::allocate(#c);
+ _cloned_cpp_vtptrs[c##_Kind] = CppVtableCloner<c>::allocate(#c); \
+ ArchivePtrMarker::mark_pointer(&_cloned_cpp_vtptrs[c##_Kind]);
#define CLONE_CPP_VTABLE(c) \
p = CppVtableCloner<c>::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<ArchiveHeapOopmapInfo>* 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<MemRegion> *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<true> 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<MemRegion> *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
+}
--- 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
--- 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 =
--- 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
--- 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<Method*>* methods, const Symbol* name) {
+bool InstanceKlass::_disable_method_binary_search = false;
+
+int InstanceKlass::quick_search(const Array<Method*>* 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() {
--- 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<Method*>* 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<Method*>* methods,
--- 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<Method*>* methods, bool set_idnums) {
+void Method::sort_methods(Array<Method*>* 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) {
--- 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<Method*>* methods, bool set_idnums = true);
+ static void sort_methods(Array<Method*>* 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);
--- 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);
--- 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);
--- 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 " \
--- 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)) \
\
/******************/ \
--- 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();
--- 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;
--- 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);
--- 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 \
--- 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);
--- /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");
+ }
+ });
+ }
+}
--- /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);
+ }
+ });
+ }
+}
--- 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;
}
/**