8655 lines
320 KiB
Diff
8655 lines
320 KiB
Diff
From f1cba2dd8fe526f4ad5ea4913154a174bd19a080 Mon Sep 17 00:00:00 2001
|
||
Date: Sat, 3 Sep 2022 14:25:11 +0000
|
||
Subject: Dynamic-CDS-Archive
|
||
|
||
---
|
||
hotspot/src/os/linux/vm/os_linux.cpp | 3 +-
|
||
hotspot/src/share/vm/cds/archiveBuilder.cpp | 807 ++++++++++++++++
|
||
hotspot/src/share/vm/cds/archiveBuilder.hpp | 368 +++++++
|
||
hotspot/src/share/vm/cds/archiveUtils.cpp | 247 +++++
|
||
hotspot/src/share/vm/cds/archiveUtils.hpp | 141 +++
|
||
hotspot/src/share/vm/cds/dumpAllocStats.cpp | 109 +++
|
||
hotspot/src/share/vm/cds/dumpAllocStats.hpp | 88 ++
|
||
hotspot/src/share/vm/cds/dynamicArchive.cpp | 412 ++++++++
|
||
hotspot/src/share/vm/cds/dynamicArchive.hpp | 54 ++
|
||
.../share/vm/classfile/classFileParser.cpp | 7 +
|
||
.../src/share/vm/classfile/classLoaderExt.hpp | 2 +-
|
||
.../share/vm/classfile/compactHashtable.cpp | 216 +++++
|
||
.../share/vm/classfile/compactHashtable.hpp | 349 +++++++
|
||
.../share/vm/classfile/sharedClassUtil.hpp | 4 +
|
||
.../src/share/vm/classfile/symbolTable.cpp | 102 +-
|
||
.../src/share/vm/classfile/symbolTable.hpp | 12 +
|
||
.../share/vm/classfile/systemDictionary.cpp | 159 +--
|
||
.../share/vm/classfile/systemDictionary.hpp | 1 +
|
||
.../vm/classfile/systemDictionaryShared.cpp | 911 ++++++++++++++++++
|
||
.../vm/classfile/systemDictionaryShared.hpp | 167 +++-
|
||
hotspot/src/share/vm/memory/allocation.hpp | 12 +
|
||
.../src/share/vm/memory/allocation.inline.hpp | 53 +-
|
||
hotspot/src/share/vm/memory/filemap.cpp | 352 +++++--
|
||
hotspot/src/share/vm/memory/filemap.hpp | 104 +-
|
||
hotspot/src/share/vm/memory/iterator.hpp | 7 +
|
||
hotspot/src/share/vm/memory/metaspace.cpp | 80 +-
|
||
hotspot/src/share/vm/memory/metaspace.hpp | 1 +
|
||
.../src/share/vm/memory/metaspaceClosure.cpp | 87 ++
|
||
.../src/share/vm/memory/metaspaceClosure.hpp | 381 ++++++++
|
||
.../src/share/vm/memory/metaspaceShared.cpp | 148 ++-
|
||
.../src/share/vm/memory/metaspaceShared.hpp | 51 +-
|
||
hotspot/src/share/vm/oops/annotations.cpp | 12 +
|
||
hotspot/src/share/vm/oops/annotations.hpp | 9 +
|
||
hotspot/src/share/vm/oops/arrayKlass.cpp | 22 +
|
||
hotspot/src/share/vm/oops/arrayKlass.hpp | 3 +-
|
||
hotspot/src/share/vm/oops/constMethod.cpp | 26 +
|
||
hotspot/src/share/vm/oops/constMethod.hpp | 8 +-
|
||
hotspot/src/share/vm/oops/constantPool.cpp | 93 +-
|
||
hotspot/src/share/vm/oops/constantPool.hpp | 12 +
|
||
hotspot/src/share/vm/oops/cpCache.cpp | 69 ++
|
||
hotspot/src/share/vm/oops/cpCache.hpp | 25 +-
|
||
hotspot/src/share/vm/oops/instanceKlass.cpp | 131 ++-
|
||
hotspot/src/share/vm/oops/instanceKlass.hpp | 12 +-
|
||
hotspot/src/share/vm/oops/klass.cpp | 83 +-
|
||
hotspot/src/share/vm/oops/klass.hpp | 10 +-
|
||
hotspot/src/share/vm/oops/klassVtable.hpp | 3 +
|
||
hotspot/src/share/vm/oops/metadata.hpp | 4 +-
|
||
hotspot/src/share/vm/oops/method.cpp | 22 +-
|
||
hotspot/src/share/vm/oops/method.hpp | 7 +-
|
||
hotspot/src/share/vm/oops/methodCounters.hpp | 7 +
|
||
hotspot/src/share/vm/oops/methodData.cpp | 9 +
|
||
hotspot/src/share/vm/oops/methodData.hpp | 5 +-
|
||
hotspot/src/share/vm/oops/objArrayKlass.cpp | 7 +
|
||
hotspot/src/share/vm/oops/objArrayKlass.hpp | 3 +-
|
||
hotspot/src/share/vm/oops/symbol.hpp | 22 +-
|
||
hotspot/src/share/vm/runtime/arguments.cpp | 142 +++
|
||
hotspot/src/share/vm/runtime/arguments.hpp | 19 +-
|
||
hotspot/src/share/vm/runtime/globals.hpp | 21 +
|
||
hotspot/src/share/vm/runtime/java.cpp | 8 +
|
||
hotspot/src/share/vm/runtime/mutexLocker.cpp | 5 +-
|
||
hotspot/src/share/vm/runtime/mutexLocker.hpp | 3 +
|
||
hotspot/src/share/vm/runtime/os.cpp | 9 +-
|
||
hotspot/src/share/vm/runtime/os.hpp | 2 +
|
||
hotspot/src/share/vm/runtime/thread.cpp | 10 +
|
||
.../share/vm/services/diagnosticCommand.cpp | 13 +
|
||
.../share/vm/services/diagnosticCommand.hpp | 23 +
|
||
hotspot/src/share/vm/utilities/array.hpp | 1 +
|
||
hotspot/src/share/vm/utilities/bitMap.cpp | 17 +-
|
||
hotspot/src/share/vm/utilities/bitMap.hpp | 1 +
|
||
.../src/share/vm/utilities/constantTag.hpp | 5 +-
|
||
.../share/vm/utilities/globalDefinitions.hpp | 11 +-
|
||
hotspot/src/share/vm/utilities/hashtable.cpp | 60 +-
|
||
hotspot/src/share/vm/utilities/hashtable.hpp | 98 +-
|
||
.../share/vm/utilities/hashtable.inline.hpp | 2 +-
|
||
hotspot/src/share/vm/utilities/ostream.cpp | 11 +
|
||
hotspot/src/share/vm/utilities/ostream.hpp | 2 +-
|
||
.../src/share/vm/utilities/resourceHash.hpp | 27 +-
|
||
77 files changed, 6234 insertions(+), 295 deletions(-)
|
||
create mode 100644 hotspot/src/share/vm/cds/archiveBuilder.cpp
|
||
create mode 100644 hotspot/src/share/vm/cds/archiveBuilder.hpp
|
||
create mode 100644 hotspot/src/share/vm/cds/archiveUtils.cpp
|
||
create mode 100644 hotspot/src/share/vm/cds/archiveUtils.hpp
|
||
create mode 100644 hotspot/src/share/vm/cds/dumpAllocStats.cpp
|
||
create mode 100644 hotspot/src/share/vm/cds/dumpAllocStats.hpp
|
||
create mode 100644 hotspot/src/share/vm/cds/dynamicArchive.cpp
|
||
create mode 100644 hotspot/src/share/vm/cds/dynamicArchive.hpp
|
||
create mode 100644 hotspot/src/share/vm/classfile/compactHashtable.cpp
|
||
create mode 100644 hotspot/src/share/vm/classfile/compactHashtable.hpp
|
||
create mode 100644 hotspot/src/share/vm/classfile/systemDictionaryShared.cpp
|
||
create mode 100644 hotspot/src/share/vm/memory/metaspaceClosure.cpp
|
||
create mode 100644 hotspot/src/share/vm/memory/metaspaceClosure.hpp
|
||
|
||
diff --git a/hotspot/src/os/linux/vm/os_linux.cpp b/hotspot/src/os/linux/vm/os_linux.cpp
|
||
index f700335a3..6dbedf5c2 100644
|
||
--- a/hotspot/src/os/linux/vm/os_linux.cpp
|
||
+++ b/hotspot/src/os/linux/vm/os_linux.cpp
|
||
@@ -2370,8 +2370,7 @@ void os::print_siginfo(outputStream* st, void* siginfo) {
|
||
#if INCLUDE_CDS
|
||
if (si && (si->si_signo == SIGBUS || si->si_signo == SIGSEGV) &&
|
||
UseSharedSpaces) {
|
||
- FileMapInfo* mapinfo = FileMapInfo::current_info();
|
||
- if (mapinfo->is_in_shared_space(si->si_addr)) {
|
||
+ if (MetaspaceShared::is_in_shared_space(si->si_addr)) {
|
||
st->print("\n\nError accessing class data sharing archive." \
|
||
" Mapped file inaccessible during execution, " \
|
||
" possible disk/network problem.");
|
||
diff --git a/hotspot/src/share/vm/cds/archiveBuilder.cpp b/hotspot/src/share/vm/cds/archiveBuilder.cpp
|
||
new file mode 100644
|
||
index 000000000..144dedfa9
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/cds/archiveBuilder.cpp
|
||
@@ -0,0 +1,807 @@
|
||
+/*
|
||
+ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 "cds/archiveBuilder.hpp"
|
||
+#include "cds/archiveUtils.hpp"
|
||
+#include "classfile/symbolTable.hpp"
|
||
+#include "classfile/systemDictionaryShared.hpp"
|
||
+#include "interpreter/abstractInterpreter.hpp"
|
||
+#include "memory/filemap.hpp"
|
||
+#include "memory/memRegion.hpp"
|
||
+#include "memory/metaspaceShared.hpp"
|
||
+#include "memory/resourceArea.hpp"
|
||
+#include "oops/instanceKlass.hpp"
|
||
+#include "oops/objArrayKlass.hpp"
|
||
+#include "runtime/arguments.hpp"
|
||
+#include "runtime/globals_extension.hpp"
|
||
+#include "runtime/sharedRuntime.hpp"
|
||
+#include "runtime/thread.hpp"
|
||
+#include "utilities/align.hpp"
|
||
+#include "utilities/bitMap.inline.hpp"
|
||
+#include "utilities/hashtable.inline.hpp"
|
||
+
|
||
+ArchiveBuilder* ArchiveBuilder::_current = NULL;
|
||
+
|
||
+ArchiveBuilder::OtherROAllocMark::~OtherROAllocMark() {
|
||
+ char* newtop = ArchiveBuilder::current()->_ro_region.top();
|
||
+ ArchiveBuilder::alloc_stats()->record_other_type(int(newtop - _oldtop), true);
|
||
+}
|
||
+
|
||
+ArchiveBuilder::SourceObjList::SourceObjList() : _ptrmap(16 * K, false) {
|
||
+ _total_bytes = 0;
|
||
+ _objs = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray<SourceObjInfo*>(128 * K, mtClassShared);
|
||
+}
|
||
+
|
||
+ArchiveBuilder::SourceObjList::~SourceObjList() {
|
||
+ delete _objs;
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::SourceObjList::append(MetaspaceClosure::Ref* enclosing_ref, SourceObjInfo* src_info) {
|
||
+ // Save this source object for copying
|
||
+ _objs->append(src_info);
|
||
+
|
||
+ // Prepare for marking the pointers in this source object
|
||
+ assert(is_aligned(_total_bytes, sizeof(address)), "must be");
|
||
+ src_info->set_ptrmap_start(_total_bytes / sizeof(address));
|
||
+ _total_bytes = align_up(_total_bytes + (uintx)src_info->size_in_bytes(), sizeof(address));
|
||
+ src_info->set_ptrmap_end(_total_bytes / sizeof(address));
|
||
+
|
||
+ BitMap::idx_t bitmap_size_needed = BitMap::idx_t(src_info->ptrmap_end());
|
||
+ if (_ptrmap.size() <= bitmap_size_needed) {
|
||
+ _ptrmap.resize((bitmap_size_needed + 1) * 2, false);
|
||
+ }
|
||
+}
|
||
+
|
||
+class PrintBitMap : public BitMapClosure {
|
||
+ public:
|
||
+ bool do_bit(BitMap::idx_t bit_offset) {
|
||
+ tty->print_cr("PrintBitMap : " SIZE_FORMAT, bit_offset);
|
||
+ return true;
|
||
+ }
|
||
+};
|
||
+
|
||
+void ArchiveBuilder::SourceObjList::remember_embedded_pointer(SourceObjInfo* src_info, MetaspaceClosure::Ref* ref) {
|
||
+ // src_obj contains a pointer. Remember the location of this pointer in _ptrmap,
|
||
+ // so that we can copy/relocate it later. E.g., if we have
|
||
+ // class Foo { intx scala; Bar* ptr; }
|
||
+ // Foo *f = 0x100;
|
||
+ // To mark the f->ptr pointer on 64-bit platform, this function is called with
|
||
+ // src_info()->obj() == 0x100
|
||
+ // ref->addr() == 0x108
|
||
+ address src_obj = src_info->obj();
|
||
+ address* field_addr = ref->addr();
|
||
+ assert(src_info->ptrmap_start() < _total_bytes, "sanity");
|
||
+ assert(src_info->ptrmap_end() <= _total_bytes, "sanity");
|
||
+ assert(*field_addr != NULL, "should have checked");
|
||
+
|
||
+ intx field_offset_in_bytes = ((address)field_addr) - src_obj;
|
||
+ DEBUG_ONLY(int src_obj_size = src_info->size_in_bytes();)
|
||
+ assert(field_offset_in_bytes >= 0, "must be");
|
||
+ assert(field_offset_in_bytes + intx(sizeof(intptr_t)) <= intx(src_obj_size), "must be");
|
||
+ assert(is_aligned(field_offset_in_bytes, sizeof(address)), "must be");
|
||
+
|
||
+ BitMap::idx_t idx = BitMap::idx_t(src_info->ptrmap_start() + (uintx)(field_offset_in_bytes / sizeof(address)));
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("remember_embedded_pointer: _ptrmap_start: " SIZE_FORMAT
|
||
+ "_ptrmap_end: " SIZE_FORMAT
|
||
+ " field: " PTR_FORMAT" -> " PTR_FORMAT
|
||
+ " bit_index: " SIZE_FORMAT " ",
|
||
+ src_info->ptrmap_start(), src_info->ptrmap_end(), p2i(src_obj), p2i(field_addr), idx);
|
||
+ }
|
||
+ _ptrmap.set_bit(BitMap::idx_t(idx));
|
||
+}
|
||
+
|
||
+class RelocateEmbeddedPointers : public BitMapClosure {
|
||
+ ArchiveBuilder* _builder;
|
||
+ address _dumped_obj;
|
||
+ BitMap::idx_t _start_idx;
|
||
+public:
|
||
+ RelocateEmbeddedPointers(ArchiveBuilder* builder, address dumped_obj, BitMap::idx_t start_idx) :
|
||
+ _builder(builder), _dumped_obj(dumped_obj), _start_idx(start_idx) {}
|
||
+
|
||
+ bool do_bit(BitMap::idx_t bit_offset) {
|
||
+ uintx FLAG_MASK = 0x03; // See comments around MetaspaceClosure::FLAG_MASK
|
||
+ size_t field_offset = size_t(bit_offset - _start_idx) * sizeof(address);
|
||
+ address* ptr_loc = (address*)(_dumped_obj + field_offset);
|
||
+ uintx old_p_and_bits = (uintx)(*ptr_loc);
|
||
+ uintx flag_bits = (old_p_and_bits & FLAG_MASK);
|
||
+ address old_p = (address)(old_p_and_bits & (~FLAG_MASK));
|
||
+ address new_p = _builder->get_dumped_addr(old_p);
|
||
+ uintx new_p_and_bits = ((uintx)new_p) | flag_bits;
|
||
+
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Ref: [" PTR_FORMAT "] -> " PTR_FORMAT " => " PTR_FORMAT,
|
||
+ p2i(ptr_loc), p2i(old_p), p2i(new_p));
|
||
+ }
|
||
+ ArchivePtrMarker::set_and_mark_pointer(ptr_loc, (address)(new_p_and_bits));
|
||
+ return true; // keep iterating the bitmap
|
||
+ }
|
||
+};
|
||
+
|
||
+void ArchiveBuilder::SourceObjList::relocate(int i, ArchiveBuilder* builder) {
|
||
+ SourceObjInfo* src_info = objs()->at(i);
|
||
+ assert(src_info->should_copy(), "must be");
|
||
+ BitMap::idx_t start = BitMap::idx_t(src_info->ptrmap_start()); // inclusive
|
||
+ BitMap::idx_t end = BitMap::idx_t(src_info->ptrmap_end()); // exclusive
|
||
+
|
||
+ RelocateEmbeddedPointers relocator(builder, src_info->dumped_addr(), start);
|
||
+ _ptrmap.iterate(&relocator, start, end);
|
||
+}
|
||
+
|
||
+ArchiveBuilder::ArchiveBuilder() :
|
||
+ _current_dump_space(NULL),
|
||
+ _buffer_bottom(NULL),
|
||
+ _last_verified_top(NULL),
|
||
+ _num_dump_regions_used(0),
|
||
+ _other_region_used_bytes(0),
|
||
+ _requested_static_archive_bottom(NULL),
|
||
+ _requested_static_archive_top(NULL),
|
||
+ _requested_dynamic_archive_bottom(NULL),
|
||
+ _requested_dynamic_archive_top(NULL),
|
||
+ _mapped_static_archive_bottom(NULL),
|
||
+ _mapped_static_archive_top(NULL),
|
||
+ _buffer_to_requested_delta(0),
|
||
+ _rw_region("rw", MAX_SHARED_DELTA),
|
||
+ _ro_region("ro", MAX_SHARED_DELTA),
|
||
+ _rw_src_objs(),
|
||
+ _ro_src_objs(),
|
||
+ _src_obj_table(INITIAL_TABLE_SIZE),
|
||
+ _num_instance_klasses(0),
|
||
+ _num_obj_array_klasses(0),
|
||
+ _num_type_array_klasses(0),
|
||
+ _estimated_metaspaceobj_bytes(0),
|
||
+ _estimated_hashtable_bytes(0) {
|
||
+ _klasses = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray<Klass*>(4 * K, mtClassShared);
|
||
+ _symbols = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray<Symbol*>(256 * K, mtClassShared);
|
||
+
|
||
+ assert(_current == NULL, "must be");
|
||
+ _current = this;
|
||
+}
|
||
+
|
||
+ArchiveBuilder::~ArchiveBuilder() {
|
||
+ assert(_current == this, "must be");
|
||
+ _current = NULL;
|
||
+
|
||
+ clean_up_src_obj_table();
|
||
+
|
||
+ for (int i = 0; i < _symbols->length(); i++) {
|
||
+ _symbols->at(i)->decrement_refcount();
|
||
+ }
|
||
+
|
||
+ delete _klasses;
|
||
+ delete _symbols;
|
||
+ if (_shared_rs.is_reserved()) {
|
||
+ _shared_rs.release();
|
||
+ }
|
||
+}
|
||
+
|
||
+bool ArchiveBuilder::gather_one_source_obj(MetaspaceClosure::Ref* enclosing_ref,
|
||
+ MetaspaceClosure::Ref* ref, bool read_only) {
|
||
+ address src_obj = ref->obj();
|
||
+ if (src_obj == NULL) {
|
||
+ return false;
|
||
+ }
|
||
+ ref->set_keep_after_pushing();
|
||
+ remember_embedded_pointer_in_copied_obj(enclosing_ref, ref);
|
||
+
|
||
+ FollowMode follow_mode = get_follow_mode(ref);
|
||
+ SourceObjInfo src_info(ref, read_only, follow_mode);
|
||
+ bool created;
|
||
+ SourceObjInfo* p = _src_obj_table.add_if_absent(src_obj, src_info, &created);
|
||
+ if (created) {
|
||
+ if (_src_obj_table.maybe_grow(MAX_TABLE_SIZE)) {
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Expanded _src_obj_table table to %d", _src_obj_table.table_size());
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ assert(p->read_only() == src_info.read_only(), "must be");
|
||
+
|
||
+ if (created && src_info.should_copy()) {
|
||
+ ref->set_user_data((void*)p);
|
||
+ if (read_only) {
|
||
+ _ro_src_objs.append(enclosing_ref, p);
|
||
+ } else {
|
||
+ _rw_src_objs.append(enclosing_ref, p);
|
||
+ }
|
||
+ return true; // Need to recurse into this ref only if we are copying it
|
||
+ } else {
|
||
+ return false;
|
||
+ }
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::iterate_sorted_roots(MetaspaceClosure* it, bool is_relocating_pointers) {
|
||
+ int i;
|
||
+
|
||
+ if (!is_relocating_pointers) {
|
||
+ // Don't relocate _symbol, so we can safely call decrement_refcount on the
|
||
+ // original symbols.
|
||
+ int num_symbols = _symbols->length();
|
||
+ for (i = 0; i < num_symbols; i++) {
|
||
+ it->push(_symbols->adr_at(i));
|
||
+ }
|
||
+ }
|
||
+
|
||
+ int num_klasses = _klasses->length();
|
||
+ for (i = 0; i < num_klasses; i++) {
|
||
+ it->push(_klasses->adr_at(i));
|
||
+ }
|
||
+
|
||
+ iterate_roots(it, is_relocating_pointers);
|
||
+}
|
||
+
|
||
+class GatherSortedSourceObjs : public MetaspaceClosure {
|
||
+ ArchiveBuilder* _builder;
|
||
+
|
||
+public:
|
||
+ GatherSortedSourceObjs(ArchiveBuilder* builder) : _builder(builder) {}
|
||
+
|
||
+ virtual bool do_ref(Ref* ref, bool read_only) {
|
||
+ return _builder->gather_one_source_obj(enclosing_ref(), ref, read_only);
|
||
+ }
|
||
+
|
||
+ virtual void do_pending_ref(Ref* ref) {
|
||
+ if (ref->obj() != NULL) {
|
||
+ _builder->remember_embedded_pointer_in_copied_obj(enclosing_ref(), ref);
|
||
+ }
|
||
+ }
|
||
+};
|
||
+
|
||
+void ArchiveBuilder::gather_source_objs() {
|
||
+ ResourceMark rm;
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Gathering all archivable objects ... ");
|
||
+ }
|
||
+ gather_klasses_and_symbols();
|
||
+ GatherSortedSourceObjs doit(this);
|
||
+ iterate_sorted_roots(&doit, /*is_relocating_pointers=*/false);
|
||
+ doit.finish();
|
||
+}
|
||
+
|
||
+bool ArchiveBuilder::is_excluded(Klass* klass) {
|
||
+ if (klass->oop_is_instance()) {
|
||
+ InstanceKlass* ik = InstanceKlass::cast(klass);
|
||
+ return SystemDictionaryShared::is_excluded_class(ik);
|
||
+ } else if (klass->oop_is_objArray()) {
|
||
+ if (DynamicDumpSharedSpaces) {
|
||
+ // Don't support archiving of array klasses for now (WHY???)
|
||
+ return true;
|
||
+ }
|
||
+ Klass* bottom = ObjArrayKlass::cast(klass)->bottom_klass();
|
||
+ if (bottom->oop_is_instance()) {
|
||
+ return SystemDictionaryShared::is_excluded_class(InstanceKlass::cast(bottom));
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return false;
|
||
+}
|
||
+
|
||
+ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref *ref) {
|
||
+ address obj = ref->obj();
|
||
+ if (MetaspaceShared::is_in_shared_space(obj)) {
|
||
+ // Don't dump existing shared metadata again.
|
||
+ return point_to_it;
|
||
+ } else if (ref->msotype() == MetaspaceObj::MethodDataType) {
|
||
+ return set_to_null;
|
||
+ } else {
|
||
+ if (ref->msotype() == MetaspaceObj::ClassType) {
|
||
+ Klass* klass = (Klass*)ref->obj();
|
||
+ assert(klass->is_klass(), "must be");
|
||
+ if (is_excluded(klass)) {
|
||
+ if (TraceDynamicCDS) {
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("Skipping class (excluded): %s", klass->external_name());
|
||
+ }
|
||
+ return set_to_null;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return make_a_copy;
|
||
+ }
|
||
+}
|
||
+
|
||
+int ArchiveBuilder::compare_symbols_by_address(Symbol** a, Symbol** b) {
|
||
+ if (a[0] < b[0]) {
|
||
+ return -1;
|
||
+ } else {
|
||
+ assert(a[0] > b[0], "Duplicated symbol unexpected");
|
||
+ return 1;
|
||
+ }
|
||
+}
|
||
+
|
||
+int ArchiveBuilder::compare_klass_by_name(Klass** a, Klass** b) {
|
||
+ return a[0]->name()->fast_compare(b[0]->name());
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::sort_klasses() {
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Sorting classes ... ");
|
||
+ }
|
||
+ _klasses->sort(compare_klass_by_name);
|
||
+}
|
||
+
|
||
+class GatherKlassesAndSymbols : public UniqueMetaspaceClosure {
|
||
+ ArchiveBuilder* _builder;
|
||
+
|
||
+public:
|
||
+ GatherKlassesAndSymbols(ArchiveBuilder* builder) : _builder(builder) { }
|
||
+
|
||
+ virtual bool do_unique_ref(Ref* ref, bool read_only) {
|
||
+ return _builder->gather_klass_and_symbol(ref, read_only);
|
||
+ }
|
||
+};
|
||
+
|
||
+void ArchiveBuilder::gather_klasses_and_symbols() {
|
||
+ ResourceMark rm;
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Gathering classes and symbols ... ");
|
||
+ }
|
||
+ GatherKlassesAndSymbols doit(this);
|
||
+ iterate_roots(&doit, false);
|
||
+ doit.finish();
|
||
+
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Number of classes %d", _num_instance_klasses + _num_obj_array_klasses + _num_type_array_klasses);
|
||
+ dynamic_cds_log->print_cr(" instance classes = %5d", _num_instance_klasses);
|
||
+ dynamic_cds_log->print_cr(" obj array classes = %5d", _num_obj_array_klasses);
|
||
+ dynamic_cds_log->print_cr(" type array classes = %5d", _num_type_array_klasses);
|
||
+ dynamic_cds_log->print_cr(" symbols = %5d", _symbols->length());
|
||
+ }
|
||
+}
|
||
+
|
||
+bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool read_only) {
|
||
+ if (ref->obj() == NULL) {
|
||
+ return false;
|
||
+ }
|
||
+ if (get_follow_mode(ref) != make_a_copy) {
|
||
+ return false;
|
||
+ }
|
||
+ if (ref->msotype() == MetaspaceObj::ClassType) {
|
||
+ Klass* klass = (Klass*)ref->obj();
|
||
+ assert(klass->is_klass(), "must be");
|
||
+ if (!is_excluded(klass)) {
|
||
+ _klasses->append(klass);
|
||
+ if (klass->oop_is_instance()) {
|
||
+ _num_instance_klasses ++;
|
||
+ } else if (klass->oop_is_objArray()) {
|
||
+ _num_obj_array_klasses ++;
|
||
+ } else {
|
||
+ assert(klass->oop_is_typeArray(), "sanity");
|
||
+ _num_type_array_klasses ++;
|
||
+ }
|
||
+ }
|
||
+ // See RunTimeSharedClassInfo::get_for()
|
||
+ _estimated_metaspaceobj_bytes += align_up(BytesPerWord, KlassAlignmentInBytes);
|
||
+ } else if (ref->msotype() == MetaspaceObj::SymbolType) {
|
||
+ // Make sure the symbol won't be GC'ed while we are dumping the archive.
|
||
+ Symbol* sym = (Symbol*)ref->obj();
|
||
+ sym->increment_refcount();
|
||
+ _symbols->append(sym);
|
||
+ }
|
||
+
|
||
+ int bytes = ref->size() * BytesPerWord;
|
||
+ _estimated_metaspaceobj_bytes += align_up(bytes, KlassAlignmentInBytes);
|
||
+ return true; // recurse
|
||
+}
|
||
+
|
||
+size_t ArchiveBuilder::estimate_archive_size() {
|
||
+ // size of the symbol table and two dictionaries, plus the RunTimeSharedClassInfo's
|
||
+ size_t symbol_table_est = SymbolTable::estimate_size_for_archive();
|
||
+ size_t dictionary_est = SystemDictionaryShared::estimate_size_for_archive();
|
||
+ _estimated_hashtable_bytes = symbol_table_est + dictionary_est;
|
||
+
|
||
+ size_t total = 0;
|
||
+
|
||
+ total += _estimated_metaspaceobj_bytes;
|
||
+ total += _estimated_hashtable_bytes;
|
||
+
|
||
+ // allow fragmentation at the end of each dump region
|
||
+ total += _total_dump_regions * ((size_t)os::vm_allocation_granularity());
|
||
+
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("_estimated_hashtable_bytes = " SIZE_FORMAT " + " SIZE_FORMAT " = " SIZE_FORMAT,
|
||
+ symbol_table_est, dictionary_est, _estimated_hashtable_bytes);
|
||
+ dynamic_cds_log->print_cr("_estimated_metaspaceobj_bytes = " SIZE_FORMAT, _estimated_metaspaceobj_bytes);
|
||
+ dynamic_cds_log->print_cr("total estimate bytes = " SIZE_FORMAT, total);
|
||
+ }
|
||
+
|
||
+ return align_up(total, (size_t)os::vm_allocation_granularity());
|
||
+}
|
||
+
|
||
+address ArchiveBuilder::reserve_buffer() {
|
||
+ size_t buffer_size = estimate_archive_size();
|
||
+ ReservedSpace rs(buffer_size, os::vm_allocation_granularity(), false);
|
||
+ if (!rs.is_reserved()) {
|
||
+ tty->print_cr("Failed to reserve " SIZE_FORMAT " bytes of output buffer.", buffer_size);
|
||
+ vm_direct_exit(0);
|
||
+ }
|
||
+
|
||
+ // buffer_bottom is the lowest address of the 2 core regions (rw, ro) when
|
||
+ // we are copying the class metadata into the buffer.
|
||
+ address buffer_bottom = (address)rs.base();
|
||
+ _shared_rs = rs;
|
||
+
|
||
+ _buffer_bottom = buffer_bottom;
|
||
+ _last_verified_top = buffer_bottom;
|
||
+ _current_dump_space = &_rw_region;
|
||
+ _num_dump_regions_used = 1;
|
||
+ _other_region_used_bytes = 0;
|
||
+ _current_dump_space->init(&_shared_rs, &_shared_vs);
|
||
+
|
||
+ ArchivePtrMarker::initialize(&_ptrmap, &_shared_vs);
|
||
+
|
||
+ // The bottom of the static archive should be mapped at this address by default.
|
||
+ _requested_static_archive_bottom = (address)MetaspaceShared::requested_base_address();
|
||
+
|
||
+ size_t static_archive_size = FileMapInfo::shared_spaces_size();
|
||
+ _requested_static_archive_top = _requested_static_archive_bottom + static_archive_size;
|
||
+
|
||
+ _mapped_static_archive_bottom = (address)MetaspaceShared::shared_metaspace_static_bottom();
|
||
+ _mapped_static_archive_top = _mapped_static_archive_bottom + static_archive_size;
|
||
+
|
||
+ _requested_dynamic_archive_bottom = align_up(_requested_static_archive_top, (size_t)os::vm_allocation_granularity());
|
||
+
|
||
+ _buffer_to_requested_delta = _requested_dynamic_archive_bottom - _buffer_bottom;
|
||
+
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Reserved output buffer space at " PTR_FORMAT " [" SIZE_FORMAT " bytes]",
|
||
+ p2i(buffer_bottom), buffer_size);
|
||
+ dynamic_cds_log->print_cr("Dynamic archive mapped space at " PTR_FORMAT, p2i(_requested_dynamic_archive_bottom));
|
||
+ }
|
||
+
|
||
+ return buffer_bottom;
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::verify_estimate_size(size_t estimate, const char* which) {
|
||
+ address bottom = _last_verified_top;
|
||
+ address top = (address)(current_dump_space()->top());
|
||
+ size_t used = size_t(top - bottom) + _other_region_used_bytes;
|
||
+ int diff = int(estimate) - int(used);
|
||
+
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("%s estimate = " SIZE_FORMAT " used = " SIZE_FORMAT "; diff = %d bytes", which, estimate, used, diff);
|
||
+ }
|
||
+ assert(diff >= 0, "Estimate is too small");
|
||
+
|
||
+ _last_verified_top = top;
|
||
+ _other_region_used_bytes = 0;
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::dump_rw_metadata() {
|
||
+ ResourceMark rm;
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Allocating RW objects ... ");
|
||
+ }
|
||
+ make_shallow_copies(&_rw_region, &_rw_src_objs);
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::dump_ro_metadata() {
|
||
+ ResourceMark rm;
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Allocating RO objects ... ");
|
||
+ }
|
||
+ start_dump_space(&_ro_region);
|
||
+ make_shallow_copies(&_ro_region, &_ro_src_objs);
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::start_dump_space(DumpRegion* next) {
|
||
+ address bottom = _last_verified_top;
|
||
+ address top = (address)(_current_dump_space->top());
|
||
+ _other_region_used_bytes += size_t(top - bottom);
|
||
+ _current_dump_space->pack(next);
|
||
+ _current_dump_space = next;
|
||
+ _num_dump_regions_used ++;
|
||
+ _last_verified_top = (address)(_current_dump_space->top());
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::patch_shared_obj_vtable() {
|
||
+ SourceObjList* objs = &_rw_src_objs;
|
||
+
|
||
+ for (int i = 0; i < objs->objs()->length(); i++) {
|
||
+ SourceObjInfo* src_info = objs->objs()->at(i);
|
||
+ address dest = src_info->dumped_addr();
|
||
+ MetaspaceClosure::Ref* ref = src_info->ref();
|
||
+ intptr_t* archived_vtable = MetaspaceShared::get_archived_vtable(ref->msotype(), dest);
|
||
+ if (archived_vtable != NULL) {
|
||
+ // When we copy archived vtable from base archive into dynamic archive's objs, we can't call
|
||
+ // virtual function before restore dynamic archive.
|
||
+ *(intptr_t**)dest = archived_vtable;
|
||
+ ArchivePtrMarker::mark_pointer((address*)dest);
|
||
+ }
|
||
+ }
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("patch vtable done (%d objects)", objs->objs()->length());
|
||
+ }
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::remember_embedded_pointer_in_copied_obj(MetaspaceClosure::Ref* enclosing_ref,
|
||
+ MetaspaceClosure::Ref* ref) {
|
||
+ assert(ref->obj() != NULL, "should have checked");
|
||
+
|
||
+ if (enclosing_ref != NULL) {
|
||
+ SourceObjInfo* src_info = (SourceObjInfo*)enclosing_ref->user_data();
|
||
+ if (src_info == NULL) {
|
||
+ // source objects of point_to_it/set_to_null types are not copied
|
||
+ // so we don't need to remember their pointers.
|
||
+ } else {
|
||
+ if (src_info->read_only()) {
|
||
+ _ro_src_objs.remember_embedded_pointer(src_info, ref);
|
||
+ } else {
|
||
+ _rw_src_objs.remember_embedded_pointer(src_info, ref);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::make_shallow_copies(DumpRegion *dump_region,
|
||
+ const ArchiveBuilder::SourceObjList* src_objs) {
|
||
+ for (int i = 0; i < src_objs->objs()->length(); i++) {
|
||
+ make_shallow_copy(dump_region, src_objs->objs()->at(i));
|
||
+ }
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("done (%d objects)", src_objs->objs()->length());
|
||
+ }
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* src_info) {
|
||
+ MetaspaceClosure::Ref* ref = src_info->ref();
|
||
+ address src = ref->obj();
|
||
+ int bytes = src_info->size_in_bytes();
|
||
+ char* dest;
|
||
+ char* oldtop;
|
||
+ char* newtop;
|
||
+
|
||
+ oldtop = dump_region->top();
|
||
+ if (ref->msotype() == MetaspaceObj::ClassType) {
|
||
+ // Save a pointer immediate in front of an InstanceKlass, so
|
||
+ // we can do a quick lookup from InstanceKlass* -> RunTimeSharedClassInfo*
|
||
+ // without building another hashtable. See RunTimeSharedClassInfo::get_for()
|
||
+ // in systemDictionaryShared.cpp.
|
||
+ Klass* klass = (Klass*)src;
|
||
+ if (klass->oop_is_instance()) {
|
||
+ dump_region->allocate(sizeof(address));
|
||
+ }
|
||
+ }
|
||
+ dest = dump_region->allocate(bytes);
|
||
+ newtop = dump_region->top();
|
||
+
|
||
+ memcpy(dest, src, bytes);
|
||
+
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Copy: " PTR_FORMAT " ==> " PTR_FORMAT " %d", p2i(src), p2i(dest), bytes);
|
||
+ }
|
||
+ src_info->set_dumped_addr((address)dest);
|
||
+
|
||
+ _alloc_stats.record(ref->msotype(), int(newtop - oldtop), src_info->read_only());
|
||
+}
|
||
+
|
||
+address ArchiveBuilder::get_dumped_addr(address src_obj) {
|
||
+ SourceObjInfo* p = _src_obj_table.lookup(src_obj);
|
||
+ assert(p != NULL, "must be");
|
||
+
|
||
+ return p->dumped_addr();
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::relocate_embedded_pointers(ArchiveBuilder::SourceObjList* src_objs) {
|
||
+ for (int i = 0; i < src_objs->objs()->length(); i++) {
|
||
+ src_objs->relocate(i, this);
|
||
+ }
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::print_stats() {
|
||
+ _alloc_stats.print_stats(int(_ro_region.used()), int(_rw_region.used()));
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::make_klasses_shareable() {
|
||
+ for (int i = 0; i < klasses()->length(); i++) {
|
||
+ Klass* k = klasses()->at(i);
|
||
+ k->remove_java_mirror();
|
||
+ if (k->oop_is_objArray()) {
|
||
+ // InstanceKlass and TypeArrayKlass will in turn call remove_unshareable_info
|
||
+ // on their array classes.
|
||
+ } else if (k->oop_is_typeArray()) {
|
||
+ k->remove_unshareable_info();
|
||
+ } else {
|
||
+ assert(k->oop_is_instance(), " must be");
|
||
+ InstanceKlass* ik = InstanceKlass::cast(k);
|
||
+ // High version introduce fast bytecode, jdk8 no need do it.
|
||
+ // MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread::current(), ik);
|
||
+ ik->remove_unshareable_info(); // assign_class_loader_type is in Klass::remove_unshareable_info
|
||
+
|
||
+ if (DebugDynamicCDS) {
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("klasses[%4d] = " PTR_FORMAT " => " PTR_FORMAT " %s", i, p2i(ik), p2i(to_requested(ik)), ik->external_name());
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+uintx ArchiveBuilder::buffer_to_offset(address p) const {
|
||
+ address requested_p = to_requested(p);
|
||
+ assert(requested_p >= _requested_static_archive_bottom, "must be");
|
||
+ return requested_p - _requested_static_archive_bottom;
|
||
+}
|
||
+
|
||
+uintx ArchiveBuilder::any_to_offset(address p) const {
|
||
+ if (is_in_mapped_static_archive(p)) {
|
||
+ assert(DynamicDumpSharedSpaces, "must be");
|
||
+ return p - _mapped_static_archive_bottom;
|
||
+ }
|
||
+ return buffer_to_offset(p);
|
||
+}
|
||
+
|
||
+// RelocateBufferToRequested --- Relocate all the pointers in rw/ro,
|
||
+// so that the archive can be mapped to the "requested" location without runtime relocation.
|
||
+//
|
||
+// - See ArchiveBuilder header for the definition of "buffer", "mapped" and "requested"
|
||
+// - ArchivePtrMarker::ptrmap() marks all the pointers in the rw/ro regions
|
||
+// - Every pointer must have one of the following values:
|
||
+// [a] NULL:
|
||
+// No relocation is needed. Remove this pointer from ptrmap so we don't need to
|
||
+// consider it at runtime.
|
||
+// [b] Points into an object X which is inside the buffer:
|
||
+// Adjust this pointer by _buffer_to_requested_delta, so it points to X
|
||
+// when the archive is mapped at the requested location.
|
||
+// [c] Points into an object Y which is inside mapped static archive:
|
||
+// - This happens only during dynamic dump
|
||
+// - Adjust this pointer by _mapped_to_requested_static_archive_delta,
|
||
+// so it points to Y when the static archive is mapped at the requested location.
|
||
+class RelocateBufferToRequested : public BitMapClosure {
|
||
+ ArchiveBuilder* _builder;
|
||
+ address _buffer_bottom;
|
||
+ intx _buffer_to_requested_delta;
|
||
+ intx _mapped_to_requested_static_archive_delta;
|
||
+ size_t _max_non_null_offset;
|
||
+
|
||
+ public:
|
||
+ RelocateBufferToRequested(ArchiveBuilder* builder) {
|
||
+ _builder = builder;
|
||
+ _buffer_bottom = _builder->buffer_bottom();
|
||
+ _buffer_to_requested_delta = builder->buffer_to_requested_delta();
|
||
+ _mapped_to_requested_static_archive_delta = builder->requested_static_archive_bottom() - builder->mapped_static_archive_bottom();
|
||
+ _max_non_null_offset = 0;
|
||
+
|
||
+ address bottom = _builder->buffer_bottom();
|
||
+ address top = _builder->buffer_top();
|
||
+ address new_bottom = bottom + _buffer_to_requested_delta;
|
||
+ address new_top = top + _buffer_to_requested_delta;
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Relocating archive from [" INTPTR_FORMAT " - " INTPTR_FORMAT "] to "
|
||
+ "[" INTPTR_FORMAT " - " INTPTR_FORMAT "]",
|
||
+ p2i(bottom), p2i(top),
|
||
+ p2i(new_bottom), p2i(new_top));
|
||
+ }
|
||
+ }
|
||
+
|
||
+ bool do_bit(size_t offset) {
|
||
+ address* p = (address*)_buffer_bottom + offset;
|
||
+ assert(_builder->is_in_buffer_space(p), "pointer must live in buffer space");
|
||
+
|
||
+ if (*p == NULL) {
|
||
+ // todo -- clear bit, etc
|
||
+ ArchivePtrMarker::ptrmap()->clear_bit(offset);
|
||
+ } else {
|
||
+ if (_builder->is_in_buffer_space(*p)) {
|
||
+ *p += _buffer_to_requested_delta;
|
||
+ // assert is in requested dynamic archive
|
||
+ } else {
|
||
+ assert(_builder->is_in_mapped_static_archive(*p), "old pointer must point inside buffer space or mapped static archive");
|
||
+ *p += _mapped_to_requested_static_archive_delta;
|
||
+ assert(_builder->is_in_requested_static_archive(*p), "new pointer must point inside requested archive");
|
||
+ }
|
||
+
|
||
+ _max_non_null_offset = offset;
|
||
+ }
|
||
+
|
||
+ return true; // keep iterating
|
||
+ }
|
||
+
|
||
+ void doit() {
|
||
+ ArchivePtrMarker::ptrmap()->iterate(this);
|
||
+ ArchivePtrMarker::compact(_max_non_null_offset);
|
||
+ }
|
||
+};
|
||
+
|
||
+void ArchiveBuilder::relocate_to_requested() {
|
||
+ ro_region()->pack();
|
||
+
|
||
+ size_t my_archive_size = buffer_top() - buffer_bottom();
|
||
+
|
||
+ assert(DynamicDumpSharedSpaces, "must be");
|
||
+ _requested_dynamic_archive_top = _requested_dynamic_archive_bottom + my_archive_size;
|
||
+ RelocateBufferToRequested patcher(this);
|
||
+ patcher.doit();
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::clean_up_src_obj_table() {
|
||
+ SrcObjTableCleaner cleaner;
|
||
+ _src_obj_table.iterate(&cleaner);
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::write_archive(FileMapInfo* mapinfo) {
|
||
+ assert(mapinfo->header()->magic() == CDS_DYNAMIC_ARCHIVE_MAGIC, "Dynamic CDS calls only");
|
||
+
|
||
+ mapinfo->write_dynamic_header();
|
||
+
|
||
+ write_region(mapinfo, MetaspaceShared::d_rw, &_rw_region, /*read_only=*/false,/*allow_exec=*/false);
|
||
+ write_region(mapinfo, MetaspaceShared::d_ro, &_ro_region, /*read_only=*/true, /*allow_exec=*/false);
|
||
+
|
||
+ char* bitmap = mapinfo->write_bitmap_region(ArchivePtrMarker::ptrmap());
|
||
+
|
||
+ if (InfoDynamicCDS && mapinfo->is_open()) {
|
||
+ print_stats();
|
||
+ }
|
||
+
|
||
+ mapinfo->close();
|
||
+ FREE_C_HEAP_ARRAY(char, bitmap, mtClassShared);
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, bool read_only, bool allow_exec) {
|
||
+ mapinfo->write_region(region_idx, dump_region->base(), dump_region->used(), dump_region->used(), read_only, allow_exec);
|
||
+}
|
||
+
|
||
+class RefRelocator: public MetaspaceClosure {
|
||
+ ArchiveBuilder* _builder;
|
||
+
|
||
+public:
|
||
+ RefRelocator(ArchiveBuilder* builder) : _builder(builder) {}
|
||
+
|
||
+ virtual bool do_ref(Ref* ref, bool read_only) {
|
||
+ if (ref->not_null()) {
|
||
+ ref->update(_builder->get_dumped_addr(ref->obj()));
|
||
+ ArchivePtrMarker::mark_pointer(ref->addr());
|
||
+ }
|
||
+ return false; // Do not recurse.
|
||
+ }
|
||
+};
|
||
+
|
||
+void ArchiveBuilder::relocate_roots() {
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Relocating external roots ... ");
|
||
+ }
|
||
+ ResourceMark rm;
|
||
+ RefRelocator doit(this);
|
||
+ iterate_sorted_roots(&doit, /*is_relocating_pointers=*/true);
|
||
+ doit.finish();
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("done");
|
||
+ }
|
||
+}
|
||
+
|
||
+void ArchiveBuilder::relocate_metaspaceobj_embedded_pointers() {
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Relocating embedded pointers in core regions ... ");
|
||
+ }
|
||
+ relocate_embedded_pointers(&_rw_src_objs);
|
||
+ relocate_embedded_pointers(&_ro_src_objs);
|
||
+}
|
||
+
|
||
+#ifndef PRODUCT
|
||
+void ArchiveBuilder::assert_is_vm_thread() {
|
||
+ assert(Thread::current()->is_VM_thread(), "ArchiveBuilder should be used only inside the VMThread");
|
||
+}
|
||
+#endif
|
||
diff --git a/hotspot/src/share/vm/cds/archiveBuilder.hpp b/hotspot/src/share/vm/cds/archiveBuilder.hpp
|
||
new file mode 100644
|
||
index 000000000..18cd3c622
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/cds/archiveBuilder.hpp
|
||
@@ -0,0 +1,368 @@
|
||
+/*
|
||
+ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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_VM_CDS_ARCHIVEBUILDER_HPP
|
||
+#define SHARE_VM_CDS_ARCHIVEBUILDER_HPP
|
||
+
|
||
+#include "cds/archiveUtils.hpp"
|
||
+#include "cds/dumpAllocStats.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
+//#include "oops/array.hpp"
|
||
+#include "oops/klass.hpp"
|
||
+#include "runtime/os.hpp"
|
||
+#include "utilities/align.hpp"
|
||
+#include "utilities/bitMap.hpp"
|
||
+#include "utilities/growableArray.hpp"
|
||
+#include "utilities/hashtable.hpp"
|
||
+#include "utilities/resourceHash.hpp"
|
||
+
|
||
+class FileMapInfo;
|
||
+// Overview of CDS archive creation (for both static??? and dynamic dump):
|
||
+//
|
||
+// [1] Load all classes (static dump: from the classlist, dynamic dump: as part of app execution)
|
||
+// [2] Allocate "output buffer"
|
||
+// [3] Copy contents of the 2 "core" regions (rw/ro) into the output buffer.
|
||
+// - allocate the cpp vtables in rw (static dump only)
|
||
+// - memcpy the MetaspaceObjs into rw/ro:
|
||
+// dump_rw_region();
|
||
+// dump_ro_region();
|
||
+// - fix all the pointers in the MetaspaceObjs to point to the copies
|
||
+// relocate_metaspaceobj_embedded_pointers()
|
||
+// [4] Copy symbol table, dictionary, etc, into the ro region
|
||
+// [5] Relocate all the pointers in rw/ro, so that the archive can be mapped to
|
||
+// the "requested" location without runtime relocation. See relocate_to_requested()
|
||
+class ArchiveBuilder : public StackObj {
|
||
+protected:
|
||
+ DumpRegion* _current_dump_space;
|
||
+ address _buffer_bottom; // for writing the contents of rw/ro regions
|
||
+ address _last_verified_top;
|
||
+ int _num_dump_regions_used;
|
||
+ size_t _other_region_used_bytes;
|
||
+
|
||
+ // These are the addresses where we will request the static and dynamic archives to be
|
||
+ // mapped at run time. If the request fails (due to ASLR), we will map the archives at
|
||
+ // os-selected addresses.
|
||
+ address _requested_static_archive_bottom; // This is determined solely by the value of
|
||
+ // SharedBaseAddress during -Xshare:dump.
|
||
+ address _requested_static_archive_top;
|
||
+ address _requested_dynamic_archive_bottom; // Used only during dynamic dump. It's placed
|
||
+ // immediately above _requested_static_archive_top.
|
||
+ address _requested_dynamic_archive_top;
|
||
+
|
||
+ // (Used only during dynamic dump) where the static archive is actually mapped. This
|
||
+ // may be different than _requested_static_archive_{bottom,top} due to ASLR
|
||
+ address _mapped_static_archive_bottom;
|
||
+ address _mapped_static_archive_top;
|
||
+
|
||
+ intx _buffer_to_requested_delta;
|
||
+
|
||
+ DumpRegion* current_dump_space() const { return _current_dump_space; }
|
||
+
|
||
+public:
|
||
+ enum FollowMode {
|
||
+ make_a_copy, point_to_it, set_to_null
|
||
+ };
|
||
+
|
||
+private:
|
||
+ class SourceObjInfo {
|
||
+ MetaspaceClosure::Ref* _ref;
|
||
+ uintx _ptrmap_start; // The bit-offset of the start of this object (inclusive)
|
||
+ uintx _ptrmap_end; // The bit-offset of the end of this object (exclusive)
|
||
+ bool _read_only;
|
||
+ FollowMode _follow_mode;
|
||
+ int _size_in_bytes;
|
||
+ MetaspaceObj::Type _msotype;
|
||
+ address _dumped_addr; // Address this->obj(), as used by the dumped archive.
|
||
+ address _orig_obj; // The value of the original object (_ref->obj()) when this
|
||
+ // SourceObjInfo was created. Note that _ref->obj() may change
|
||
+ // later if _ref is relocated.
|
||
+
|
||
+ public:
|
||
+ SourceObjInfo(MetaspaceClosure::Ref* ref, bool read_only, FollowMode follow_mode) :
|
||
+ _ref(ref), _ptrmap_start(0), _ptrmap_end(0), _read_only(read_only), _follow_mode(follow_mode),
|
||
+ _size_in_bytes(ref->size() * BytesPerWord), _msotype(ref->msotype()),
|
||
+ _orig_obj(ref->obj()) {
|
||
+ if (follow_mode == point_to_it) {
|
||
+ _dumped_addr = ref->obj();
|
||
+ } else {
|
||
+ _dumped_addr = NULL;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ bool should_copy() const { return _follow_mode == make_a_copy; }
|
||
+ MetaspaceClosure::Ref* ref() const { return _ref; }
|
||
+ void set_dumped_addr(address dumped_addr) {
|
||
+ assert(should_copy(), "must be");
|
||
+ assert(_dumped_addr == NULL, "cannot be copied twice");
|
||
+ assert(dumped_addr != NULL, "must be a valid copy");
|
||
+ _dumped_addr = dumped_addr;
|
||
+ }
|
||
+ void set_ptrmap_start(uintx v) { _ptrmap_start = v; }
|
||
+ void set_ptrmap_end(uintx v) { _ptrmap_end = v; }
|
||
+ uintx ptrmap_start() const { return _ptrmap_start; } // inclusive
|
||
+ uintx ptrmap_end() const { return _ptrmap_end; } // exclusive
|
||
+ bool read_only() const { return _read_only; }
|
||
+ int size_in_bytes() const { return _size_in_bytes; }
|
||
+ address orig_obj() const { return _orig_obj; }
|
||
+ address dumped_addr() const { return _dumped_addr; }
|
||
+ MetaspaceObj::Type msotype() const { return _msotype; }
|
||
+
|
||
+ // convenience accessor
|
||
+ address obj() const { return ref()->obj(); }
|
||
+ };
|
||
+
|
||
+ class SourceObjList {
|
||
+ uintx _total_bytes;
|
||
+ GrowableArray<SourceObjInfo*>* _objs; // Source objects to be archived
|
||
+ BitMap _ptrmap; // Marks the addresses of the pointer fields
|
||
+ // in the source objects
|
||
+ public:
|
||
+ SourceObjList();
|
||
+ ~SourceObjList();
|
||
+ GrowableArray<SourceObjInfo*>* objs() const { return _objs; }
|
||
+
|
||
+ void append(MetaspaceClosure::Ref* enclosing_ref, SourceObjInfo* src_info);
|
||
+ void remember_embedded_pointer(SourceObjInfo* pointing_obj, MetaspaceClosure::Ref* ref);
|
||
+ void relocate(int i, ArchiveBuilder* builder);
|
||
+
|
||
+ // convenience accessor
|
||
+ SourceObjInfo* at(int i) const { return objs()->at(i); }
|
||
+ };
|
||
+
|
||
+ class SrcObjTableCleaner {
|
||
+ public:
|
||
+ bool do_entry(address key, const SourceObjInfo* value) {
|
||
+ delete value->ref();
|
||
+ return true;
|
||
+ }
|
||
+ };
|
||
+
|
||
+ static const int INITIAL_TABLE_SIZE = 15889;
|
||
+ static const int MAX_TABLE_SIZE = 1000000;
|
||
+
|
||
+ ReservedSpace _shared_rs;
|
||
+ VirtualSpace _shared_vs;
|
||
+
|
||
+ DumpRegion _rw_region;
|
||
+ DumpRegion _ro_region;
|
||
+ BitMap _ptrmap;
|
||
+
|
||
+ SourceObjList _rw_src_objs; // objs to put in rw region
|
||
+ SourceObjList _ro_src_objs; // objs to put in ro region
|
||
+ KVHashtable<address, SourceObjInfo, mtClassShared> _src_obj_table;
|
||
+ GrowableArray<Klass*>* _klasses;
|
||
+ GrowableArray<Symbol*>* _symbols;
|
||
+
|
||
+ // statistics
|
||
+ int _num_instance_klasses;
|
||
+ int _num_obj_array_klasses;
|
||
+ int _num_type_array_klasses;
|
||
+ DumpAllocStats _alloc_stats;
|
||
+
|
||
+ // For global access.
|
||
+ static ArchiveBuilder* _current;
|
||
+
|
||
+public:
|
||
+ // Use this when you allocate space outside of ArchiveBuilder::dump_{rw,ro}_region.
|
||
+ // These are usually for misc tables that are allocated in the RO space.
|
||
+ class OtherROAllocMark {
|
||
+ char* _oldtop;
|
||
+ public:
|
||
+ OtherROAllocMark() {
|
||
+ _oldtop = _current->_ro_region.top();
|
||
+ }
|
||
+ ~OtherROAllocMark();
|
||
+ };
|
||
+
|
||
+private:
|
||
+ FollowMode get_follow_mode(MetaspaceClosure::Ref *ref);
|
||
+
|
||
+ void iterate_sorted_roots(MetaspaceClosure* it, bool is_relocating_pointers);
|
||
+ void sort_klasses();
|
||
+ static int compare_symbols_by_address(Symbol** a, Symbol** b);
|
||
+ static int compare_klass_by_name(Klass** a, Klass** b);
|
||
+
|
||
+ bool is_excluded(Klass* k);
|
||
+ void clean_up_src_obj_table();
|
||
+
|
||
+ void make_shallow_copies(DumpRegion *dump_region, const SourceObjList* src_objs);
|
||
+ void make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* src_info);
|
||
+ void relocate_embedded_pointers(SourceObjList* src_objs);
|
||
+
|
||
+protected:
|
||
+ virtual void iterate_roots(MetaspaceClosure* it, bool is_relocating_pointers) = 0;
|
||
+
|
||
+ // Conservative estimate for number of bytes needed for:
|
||
+ size_t _estimated_metaspaceobj_bytes; // all archived MetaspaceObj's.
|
||
+ size_t _estimated_hashtable_bytes; // symbol table and dictionaries
|
||
+
|
||
+ size_t estimate_archive_size();
|
||
+
|
||
+ static const int _total_dump_regions = 2;
|
||
+
|
||
+ void start_dump_space(DumpRegion* next);
|
||
+ void verify_estimate_size(size_t estimate, const char* which);
|
||
+
|
||
+public:
|
||
+ address reserve_buffer();
|
||
+
|
||
+ address buffer_bottom() const { return _buffer_bottom; }
|
||
+ address buffer_top() const { return (address)current_dump_space()->top(); }
|
||
+ address requested_static_archive_bottom() const { return _requested_static_archive_bottom; }
|
||
+ address mapped_static_archive_bottom() const { return _mapped_static_archive_bottom; }
|
||
+ intx buffer_to_requested_delta() const { return _buffer_to_requested_delta; }
|
||
+
|
||
+ bool is_in_buffer_space(address p) const {
|
||
+ return (buffer_bottom() <= p && p < buffer_top());
|
||
+ }
|
||
+
|
||
+ template <typename T> bool is_in_buffer_space(T obj) const {
|
||
+ return is_in_buffer_space(address(obj));
|
||
+ }
|
||
+
|
||
+ template <typename T> bool is_in_requested_static_archive(T p) const {
|
||
+ return _requested_static_archive_bottom <= (address)p && (address)p < _requested_static_archive_top;
|
||
+ }
|
||
+
|
||
+ template <typename T> bool is_in_mapped_static_archive(T p) const {
|
||
+ return _mapped_static_archive_bottom <= (address)p && (address)p < _mapped_static_archive_top;
|
||
+ }
|
||
+
|
||
+ template <typename T> T to_requested(T obj) const {
|
||
+ assert(is_in_buffer_space(obj), "must be");
|
||
+ return (T)(address(obj) + _buffer_to_requested_delta);
|
||
+ }
|
||
+
|
||
+public:
|
||
+ static const uintx MAX_SHARED_DELTA = 0x7FFFFFFF;
|
||
+
|
||
+ // The address p points to an object inside the output buffer. When the archive is mapped
|
||
+ // at the requested address, what's the offset of this object from _requested_static_archive_bottom?
|
||
+ uintx buffer_to_offset(address p) const;
|
||
+
|
||
+ // Same as buffer_to_offset, except that the address p points to either (a) an object
|
||
+ // inside the output buffer, or (b), an object in the currently mapped static archive.
|
||
+ uintx any_to_offset(address p) const;
|
||
+
|
||
+ template <typename T>
|
||
+ u4 buffer_to_offset_u4(T p) const {
|
||
+ uintx offset = buffer_to_offset((address)p);
|
||
+ guarantee(offset <= MAX_SHARED_DELTA, "must be 32-bit offset");
|
||
+ return (u4)offset;
|
||
+ }
|
||
+
|
||
+ template <typename T>
|
||
+ u4 any_to_offset_u4(T p) const {
|
||
+ uintx offset = any_to_offset((address)p);
|
||
+ guarantee(offset <= MAX_SHARED_DELTA, "must be 32-bit offset");
|
||
+ return (u4)offset;
|
||
+ }
|
||
+
|
||
+ static void assert_is_vm_thread() PRODUCT_RETURN;
|
||
+
|
||
+public:
|
||
+ ArchiveBuilder();
|
||
+ ~ArchiveBuilder();
|
||
+
|
||
+ void gather_klasses_and_symbols();
|
||
+ void replace_klass_in_constanPool();
|
||
+ void gather_source_objs();
|
||
+ bool gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool read_only);
|
||
+ bool gather_one_source_obj(MetaspaceClosure::Ref* enclosing_ref, MetaspaceClosure::Ref* ref, bool read_only);
|
||
+ void remember_embedded_pointer_in_copied_obj(MetaspaceClosure::Ref* enclosing_ref, MetaspaceClosure::Ref* ref);
|
||
+
|
||
+ DumpRegion* rw_region() { return &_rw_region; }
|
||
+ DumpRegion* ro_region() { return &_ro_region; }
|
||
+
|
||
+ static char* rw_region_alloc(size_t num_bytes) {
|
||
+ return current()->rw_region()->allocate(num_bytes);
|
||
+ }
|
||
+ static char* ro_region_alloc(size_t num_bytes) {
|
||
+ return current()->ro_region()->allocate(num_bytes);
|
||
+ }
|
||
+
|
||
+ template <typename T>
|
||
+ static Array<T>* new_ro_array(int length) {
|
||
+ size_t byte_size = Array<T>::byte_sizeof(length);
|
||
+ Array<T>* array = (Array<T>*)ro_region_alloc(byte_size);
|
||
+ array->initialize(length);
|
||
+ return array;
|
||
+ }
|
||
+
|
||
+ template <typename T>
|
||
+ static Array<T>* new_rw_array(int length) {
|
||
+ size_t byte_size = Array<T>::byte_sizeof(length);
|
||
+ Array<T>* array = (Array<T>*)rw_region_alloc(byte_size);
|
||
+ array->initialize(length);
|
||
+ return array;
|
||
+ }
|
||
+
|
||
+ template <typename T>
|
||
+ static size_t ro_array_bytesize(int length) {
|
||
+ size_t byte_size = Array<T>::byte_sizeof(length);
|
||
+ return align_up(byte_size, KlassAlignmentInBytes);
|
||
+ }
|
||
+
|
||
+ void dump_rw_metadata();
|
||
+ void dump_ro_metadata();
|
||
+ void relocate_metaspaceobj_embedded_pointers();
|
||
+ void relocate_roots();
|
||
+ void make_klasses_shareable();
|
||
+ void relocate_to_requested();
|
||
+ void write_archive(FileMapInfo* mapinfo);
|
||
+ void write_region(FileMapInfo* mapinfo, int region_idx, DumpRegion* dump_region, bool read_only, bool allow_exec);
|
||
+ address get_dumped_addr(address src_obj);
|
||
+ void patch_shared_obj_vtable();
|
||
+
|
||
+ // All klasses and symbols that will be copied into the archive
|
||
+ GrowableArray<Klass*>* klasses() const { return _klasses; }
|
||
+ GrowableArray<Symbol*>* symbols() const { return _symbols; }
|
||
+
|
||
+ static bool is_active() {
|
||
+ return (_current != NULL);
|
||
+ }
|
||
+
|
||
+ static ArchiveBuilder* current() {
|
||
+ assert_is_vm_thread();
|
||
+ assert(_current != NULL, "ArchiveBuilder must be active");
|
||
+ return _current;
|
||
+ }
|
||
+
|
||
+ static DumpAllocStats* alloc_stats() {
|
||
+ return &(current()->_alloc_stats);
|
||
+ }
|
||
+
|
||
+ static Symbol* get_relocated_symbol(Symbol* orig_symbol) {
|
||
+ return (Symbol*)current()->get_dumped_addr((address)orig_symbol);
|
||
+ }
|
||
+
|
||
+ static CompactHashtableStats* symbol_stats() {
|
||
+ return alloc_stats()->symbol_stats();
|
||
+ }
|
||
+
|
||
+ void print_stats();
|
||
+};
|
||
+
|
||
+#endif // SHARE_VM_CDS_ARCHIVEBUILDER_HPP
|
||
diff --git a/hotspot/src/share/vm/cds/archiveUtils.cpp b/hotspot/src/share/vm/cds/archiveUtils.cpp
|
||
new file mode 100644
|
||
index 000000000..88c04241d
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/cds/archiveUtils.cpp
|
||
@@ -0,0 +1,247 @@
|
||
+/*
|
||
+ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 "cds/archiveBuilder.hpp"
|
||
+#include "cds/archiveUtils.hpp"
|
||
+#include "cds/dynamicArchive.hpp"
|
||
+#include "classfile/systemDictionaryShared.hpp"
|
||
+#include "memory/filemap.hpp"
|
||
+#include "memory/resourceArea.hpp"
|
||
+#include "runtime/arguments.hpp"
|
||
+#include "utilities/bitMap.inline.hpp"
|
||
+#include "utilities/align.hpp"
|
||
+
|
||
+BitMap* ArchivePtrMarker::_ptrmap = NULL;
|
||
+VirtualSpace* ArchivePtrMarker::_vs;
|
||
+
|
||
+bool ArchivePtrMarker::_compacted;
|
||
+
|
||
+void ArchivePtrMarker::initialize(BitMap* ptrmap, VirtualSpace* vs) {
|
||
+ assert(_ptrmap == NULL, "initialize only once");
|
||
+ _vs = vs;
|
||
+ _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->resize(estimated_archive_size / sizeof(intptr_t), false);
|
||
+}
|
||
+
|
||
+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;
|
||
+ // We don't want any pointer that points to very bottom of the archive, otherwise when
|
||
+ // MetaspaceShared::default_base_address()==0, we can't distinguish between a pointer
|
||
+ // to nothing (NULL) vs a pointer to an objects that happens to be at the very bottom
|
||
+ // of the archive.
|
||
+ assert(value != (address)ptr_base(), "don't point to the bottom of the archive");
|
||
+
|
||
+ 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, false);
|
||
+ }
|
||
+ assert(idx < _ptrmap->size(), "must be");
|
||
+ _ptrmap->set_bit(idx);
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Marking pointer [" PTR_FORMAT "] -> " PTR_FORMAT " @ " SIZE_FORMAT_W(5), p2i(ptr_loc), p2i(*ptr_loc), idx);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+void ArchivePtrMarker::clear_pointer(address* ptr_loc) {
|
||
+ assert(_ptrmap != NULL, "not initialized");
|
||
+ assert(!_compacted, "cannot clear anymore");
|
||
+
|
||
+ assert(ptr_base() <= ptr_loc && ptr_loc < ptr_end(), "must be");
|
||
+ assert(uintx(ptr_loc) % sizeof(intptr_t) == 0, "pointers must be stored in aligned addresses");
|
||
+ size_t idx = ptr_loc - ptr_base();
|
||
+ assert(idx < _ptrmap->size(), "cannot clear pointers that have not been marked");
|
||
+ _ptrmap->clear_bit(idx);
|
||
+ if (TraceDynamicCDS)
|
||
+ dynamic_cds_log->print_cr("Clearing pointer [" PTR_FORMAT "] -> " PTR_FORMAT " @ " SIZE_FORMAT_W(5), p2i(ptr_loc), p2i(*ptr_loc), idx);
|
||
+}
|
||
+
|
||
+class ArchivePtrBitmapCleaner: public BitMapClosure {
|
||
+ BitMap* _ptrmap;
|
||
+ address* _ptr_base;
|
||
+ address _relocatable_base;
|
||
+ address _relocatable_end;
|
||
+ size_t _max_non_null_offset;
|
||
+
|
||
+public:
|
||
+ ArchivePtrBitmapCleaner(BitMap* 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);
|
||
+ }
|
||
+
|
||
+ 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, false);
|
||
+ _compacted = true;
|
||
+}
|
||
+
|
||
+char* DumpRegion::expand_top_to(char* newtop) {
|
||
+ assert(is_allocatable(), "must be initialized and not packed");
|
||
+ assert(newtop >= _top, "must not grow backwards");
|
||
+ if (newtop > _end) {
|
||
+ vm_exit_during_initialization("Unable to allocate memory",
|
||
+ "Please reduce the number of shared classes.");
|
||
+ ShouldNotReachHere();
|
||
+ }
|
||
+
|
||
+ commit_to(newtop);
|
||
+ _top = newtop;
|
||
+
|
||
+ if (_max_delta > 0) {
|
||
+ uintx delta = ArchiveBuilder::current()->buffer_to_offset((address)(newtop-1));
|
||
+ if (delta > _max_delta) {
|
||
+ // This is just a sanity check and should not appear in any real world usage. This
|
||
+ // happens only if you allocate more than 2GB of shared objects and would require
|
||
+ // millions of shared classes.
|
||
+ vm_exit_during_initialization("Out of memory in the CDS archive",
|
||
+ "Please reduce the number of shared classes.");
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return _top;
|
||
+}
|
||
+
|
||
+void DumpRegion::commit_to(char* newtop) {
|
||
+ Arguments::assert_is_dumping_archive();
|
||
+ char* base = _rs->base();
|
||
+ size_t need_committed_size = newtop - base;
|
||
+ size_t has_committed_size = _vs->committed_size();
|
||
+ if (need_committed_size < has_committed_size) {
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ size_t min_bytes = need_committed_size - has_committed_size;
|
||
+ size_t preferred_bytes = 1 * M;
|
||
+ size_t uncommitted = _vs->reserved_size() - has_committed_size;
|
||
+
|
||
+ size_t commit = MAX2(min_bytes, preferred_bytes);
|
||
+ commit = MIN2(commit, uncommitted);
|
||
+ assert(commit <= uncommitted, "sanity");
|
||
+
|
||
+ if (!_vs->expand_by(commit, false)) {
|
||
+ vm_exit_during_initialization(err_msg("Failed to expand shared space to " SIZE_FORMAT " bytes",
|
||
+ need_committed_size));
|
||
+ }
|
||
+
|
||
+ if (DebugDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Expanding shared spaces by " SIZE_FORMAT_W(7) " bytes [total " SIZE_FORMAT_W(9) " bytes ending at %p]",
|
||
+ commit, _vs->actual_committed_size(), _vs->high());
|
||
+ }
|
||
+}
|
||
+
|
||
+char* DumpRegion::allocate(size_t num_bytes) {
|
||
+ char* p = (char*)align_up(_top, (size_t)KlassAlignmentInBytes);
|
||
+ char* newtop = p + align_up(num_bytes, (size_t)KlassAlignmentInBytes);
|
||
+ expand_top_to(newtop);
|
||
+ memset(p, 0, newtop - p);
|
||
+ 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::init(ReservedSpace* rs, VirtualSpace* vs) {
|
||
+ _rs = rs;
|
||
+ _vs = vs;
|
||
+ // Start with 0 committed bytes. The memory will be committed as needed.
|
||
+ if (!_vs->initialize(*_rs, 0)) {
|
||
+ fatal("Unable to allocate memory for shared space");
|
||
+ }
|
||
+ _base = _top = _rs->base();
|
||
+ _end = _rs->base() + _rs->size();
|
||
+}
|
||
+
|
||
+void DumpRegion::pack(DumpRegion* next) {
|
||
+ assert(!is_packed(), "sanity");
|
||
+ _end = (char*)align_up(_top, (size_t)os::vm_allocation_granularity());
|
||
+ _is_packed = true;
|
||
+ if (next != NULL) {
|
||
+ next->_rs = _rs;
|
||
+ next->_vs = _vs;
|
||
+ next->_base = next->_top = this->_end;
|
||
+ next->_end = _rs->base() + _rs->size();
|
||
+ }
|
||
+}
|
||
+
|
||
+void DynamicWriteClosure::do_region(u_char* start, size_t size) {
|
||
+ assert((intptr_t)start % sizeof(intptr_t) == 0, "bad alignment");
|
||
+ assert(size % sizeof(intptr_t) == 0, "bad size");
|
||
+ do_tag((int)size);
|
||
+ while (size > 0) {
|
||
+ _dump_region->append_intptr_t(*(intptr_t*)start, true);
|
||
+ start += sizeof(intptr_t);
|
||
+ size -= sizeof(intptr_t);
|
||
+ }
|
||
+}
|
||
diff --git a/hotspot/src/share/vm/cds/archiveUtils.hpp b/hotspot/src/share/vm/cds/archiveUtils.hpp
|
||
new file mode 100644
|
||
index 000000000..55c2431a0
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/cds/archiveUtils.hpp
|
||
@@ -0,0 +1,141 @@
|
||
+/*
|
||
+ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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_VM_CDS_ARCHIVEUTILS_HPP
|
||
+#define SHARE_VM_CDS_ARCHIVEUTILS_HPP
|
||
+
|
||
+#include "memory/iterator.hpp"
|
||
+#include "runtime/virtualspace.hpp"
|
||
+#include "utilities/bitMap.hpp"
|
||
+
|
||
+class ArchivePtrMarker : AllStatic {
|
||
+ static BitMap* _ptrmap;
|
||
+ static VirtualSpace* _vs;
|
||
+
|
||
+ // 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;
|
||
+
|
||
+ static address* ptr_base() { return (address*)_vs->low(); } // committed lower bound (inclusive)
|
||
+ static address* ptr_end() { return (address*)_vs->high(); } // committed upper bound (exclusive)
|
||
+
|
||
+public:
|
||
+ static void initialize(BitMap* ptrmap, VirtualSpace* vs);
|
||
+ static void mark_pointer(address* ptr_loc);
|
||
+ static void clear_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);
|
||
+ }
|
||
+
|
||
+ template <typename T>
|
||
+ static void set_and_mark_pointer(T* ptr_loc, T ptr_value) {
|
||
+ *ptr_loc = ptr_value;
|
||
+ mark_pointer(ptr_loc);
|
||
+ }
|
||
+
|
||
+ static BitMap* ptrmap() {
|
||
+ return _ptrmap;
|
||
+ }
|
||
+};
|
||
+
|
||
+class DumpRegion {
|
||
+private:
|
||
+ const char* _name;
|
||
+ char* _base;
|
||
+ char* _top;
|
||
+ char* _end;
|
||
+ uintx _max_delta;
|
||
+ bool _is_packed;
|
||
+ ReservedSpace* _rs;
|
||
+ VirtualSpace* _vs;
|
||
+
|
||
+ void commit_to(char* newtop);
|
||
+
|
||
+public:
|
||
+ DumpRegion(const char* name, uintx max_delta = 0)
|
||
+ : _name(name), _base(NULL), _top(NULL), _end(NULL),
|
||
+ _max_delta(max_delta), _is_packed(false) {}
|
||
+
|
||
+ char* expand_top_to(char* newtop);
|
||
+ char* allocate(size_t num_bytes);
|
||
+
|
||
+ void append_intptr_t(intptr_t n, bool need_to_mark = false);
|
||
+
|
||
+ char* base() const { return _base; }
|
||
+ char* top() const { return _top; }
|
||
+ char* end() const { return _end; }
|
||
+ size_t reserved() const { return _end - _base; }
|
||
+ size_t used() const { return _top - _base; }
|
||
+ bool is_packed() const { return _is_packed; }
|
||
+ bool is_allocatable() const {
|
||
+ return !is_packed() && _base != NULL;
|
||
+ }
|
||
+
|
||
+ void print(size_t total_bytes) const;
|
||
+ void print_out_of_space_msg(const char* failing_region, size_t needed_bytes);
|
||
+
|
||
+ void init(ReservedSpace* rs, VirtualSpace* vs);
|
||
+
|
||
+ void pack(DumpRegion* next = NULL);
|
||
+
|
||
+ bool contains(char* p) const {
|
||
+ return base() <= p && p < top();
|
||
+ }
|
||
+};
|
||
+
|
||
+// Closure for serializing initialization data out to a data area to be
|
||
+// written to the shared file.
|
||
+
|
||
+class DynamicWriteClosure : public SerializeClosure {
|
||
+private:
|
||
+ DumpRegion* _dump_region;
|
||
+
|
||
+public:
|
||
+ DynamicWriteClosure(DumpRegion* r) {
|
||
+ _dump_region = r;
|
||
+ }
|
||
+
|
||
+ void do_ptr(void** p) {
|
||
+ _dump_region->append_intptr_t((intptr_t)*p, true);
|
||
+ }
|
||
+
|
||
+ void do_u4(u4* p) {
|
||
+ _dump_region->append_intptr_t((intptr_t)(*p));
|
||
+ }
|
||
+
|
||
+ void do_tag(int tag) {
|
||
+ _dump_region->append_intptr_t((intptr_t)tag);
|
||
+ }
|
||
+
|
||
+ //void do_oop(oop* o);
|
||
+ void do_region(u_char* start, size_t size);
|
||
+ bool reading() const { return false; }
|
||
+};
|
||
+
|
||
+#endif // SHARE_VM_CDS_ARCHIVEUTILS_HPP
|
||
diff --git a/hotspot/src/share/vm/cds/dumpAllocStats.cpp b/hotspot/src/share/vm/cds/dumpAllocStats.cpp
|
||
new file mode 100644
|
||
index 000000000..e9146555d
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/cds/dumpAllocStats.cpp
|
||
@@ -0,0 +1,109 @@
|
||
+/*
|
||
+ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 "cds/dumpAllocStats.hpp"
|
||
+
|
||
+// Returns numerator/denominator as percentage value from 0 to 100. If denominator
|
||
+// is zero, return 0.0.
|
||
+static inline double percent_of(int numerator, int denominator) {
|
||
+ return denominator != 0 ? (double)numerator / denominator * 100.0 : 0.0;
|
||
+}
|
||
+
|
||
+void DumpAllocStats::print_stats(int ro_all, int rw_all) {
|
||
+ if (!DebugDynamicCDS) {
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ // symbols
|
||
+ _counts[RO][SymbolHashentryType] = _symbol_stats.hashentry_count;
|
||
+ _bytes [RO][SymbolHashentryType] = _symbol_stats.hashentry_bytes;
|
||
+
|
||
+ _counts[RO][SymbolBucketType] = _symbol_stats.bucket_count;
|
||
+ _bytes [RO][SymbolBucketType] = _symbol_stats.bucket_bytes;
|
||
+
|
||
+ // prevent divide-by-zero
|
||
+ if (ro_all < 1) {
|
||
+ ro_all = 1;
|
||
+ }
|
||
+ if (rw_all < 1) {
|
||
+ rw_all = 1;
|
||
+ }
|
||
+
|
||
+ int all_ro_count = 0;
|
||
+ int all_ro_bytes = 0;
|
||
+ int all_rw_count = 0;
|
||
+ int all_rw_bytes = 0;
|
||
+
|
||
+// To make fmt_stats be a syntactic constant (for format warnings), use #define.
|
||
+#define fmt_stats "%-20s: %8d %10d %5.1f | %8d %10d %5.1f | %8d %10d %5.1f"
|
||
+ const char *sep = "--------------------+---------------------------+---------------------------+--------------------------";
|
||
+ const char *hdr = " ro_cnt ro_bytes % | rw_cnt rw_bytes % | all_cnt all_bytes %";
|
||
+
|
||
+ dynamic_cds_log->print_cr("Detailed metadata info (excluding heap regions):");
|
||
+ dynamic_cds_log->print_cr("%s", hdr);
|
||
+ dynamic_cds_log->print_cr("%s", sep);
|
||
+ for (int type = 0; type < int(_number_of_types); type ++) {
|
||
+ const char *name = type_name((Type)type);
|
||
+ int ro_count = _counts[RO][type];
|
||
+ int ro_bytes = _bytes [RO][type];
|
||
+ int rw_count = _counts[RW][type];
|
||
+ int rw_bytes = _bytes [RW][type];
|
||
+ int count = ro_count + rw_count;
|
||
+ int bytes = ro_bytes + rw_bytes;
|
||
+
|
||
+ double ro_perc = percent_of(ro_bytes, ro_all);
|
||
+ double rw_perc = percent_of(rw_bytes, rw_all);
|
||
+ double perc = percent_of(bytes, ro_all + rw_all);
|
||
+
|
||
+ dynamic_cds_log->print_cr(fmt_stats, name,
|
||
+ ro_count, ro_bytes, ro_perc,
|
||
+ rw_count, rw_bytes, rw_perc,
|
||
+ count, bytes, perc);
|
||
+
|
||
+ all_ro_count += ro_count;
|
||
+ all_ro_bytes += ro_bytes;
|
||
+ all_rw_count += rw_count;
|
||
+ all_rw_bytes += rw_bytes;
|
||
+ }
|
||
+
|
||
+ int all_count = all_ro_count + all_rw_count;
|
||
+ int all_bytes = all_ro_bytes + all_rw_bytes;
|
||
+
|
||
+ double all_ro_perc = percent_of(all_ro_bytes, ro_all);
|
||
+ double all_rw_perc = percent_of(all_rw_bytes, rw_all);
|
||
+ double all_perc = percent_of(all_bytes, ro_all + rw_all);
|
||
+
|
||
+ dynamic_cds_log->print_cr("%s", sep);
|
||
+ dynamic_cds_log->print_cr(fmt_stats, "Total",
|
||
+ all_ro_count, all_ro_bytes, all_ro_perc,
|
||
+ all_rw_count, all_rw_bytes, all_rw_perc,
|
||
+ all_count, all_bytes, all_perc);
|
||
+
|
||
+ assert(all_ro_bytes == ro_all, "everything should have been counted");
|
||
+ assert(all_rw_bytes == rw_all, "everything should have been counted");
|
||
+
|
||
+#undef fmt_stats
|
||
+}
|
||
diff --git a/hotspot/src/share/vm/cds/dumpAllocStats.hpp b/hotspot/src/share/vm/cds/dumpAllocStats.hpp
|
||
new file mode 100644
|
||
index 000000000..2f9247bcb
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/cds/dumpAllocStats.hpp
|
||
@@ -0,0 +1,88 @@
|
||
+/*
|
||
+ * Copyright (c) 2020, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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_VM_CDS_DUMPALLOCSTATS_HPP
|
||
+#define SHARE_VM_CDS_DUMPALLOCSTATS_HPP
|
||
+
|
||
+#include "classfile/compactHashtable.hpp"
|
||
+#include "memory/allocation.hpp"
|
||
+
|
||
+// This is for dumping detailed statistics for the allocations
|
||
+// in the shared spaces.
|
||
+class DumpAllocStats : public ResourceObj {
|
||
+public:
|
||
+ // Here's poor man's enum inheritance
|
||
+#define SHAREDSPACE_OBJ_TYPES_DO(f) \
|
||
+ METASPACE_OBJ_TYPES_DO(f) \
|
||
+ f(SymbolHashentry) \
|
||
+ f(SymbolBucket) \
|
||
+ f(Other)
|
||
+
|
||
+ enum Type {
|
||
+ // Types are MetaspaceObj::ClassType, MetaspaceObj::SymbolType, etc
|
||
+ SHAREDSPACE_OBJ_TYPES_DO(METASPACE_OBJ_TYPE_DECLARE)
|
||
+ _number_of_types
|
||
+ };
|
||
+
|
||
+ static const char* type_name(Type type) {
|
||
+ switch(type) {
|
||
+ SHAREDSPACE_OBJ_TYPES_DO(METASPACE_OBJ_TYPE_NAME_CASE)
|
||
+ default:
|
||
+ ShouldNotReachHere();
|
||
+ return NULL;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ CompactHashtableStats _symbol_stats;
|
||
+
|
||
+ int _counts[2][_number_of_types];
|
||
+ int _bytes [2][_number_of_types];
|
||
+
|
||
+public:
|
||
+ enum { RO = 0, RW = 1 };
|
||
+
|
||
+ DumpAllocStats() {
|
||
+ memset(_counts, 0, sizeof(_counts));
|
||
+ memset(_bytes, 0, sizeof(_bytes));
|
||
+ };
|
||
+
|
||
+ CompactHashtableStats* symbol_stats() { return &_symbol_stats; }
|
||
+
|
||
+ void record(MetaspaceObj::Type type, int byte_size, bool read_only) {
|
||
+ assert(int(type) >= 0 && type < MetaspaceObj::_number_of_types, "sanity");
|
||
+ int which = (read_only) ? RO : RW;
|
||
+ _counts[which][type] ++;
|
||
+ _bytes [which][type] += byte_size;
|
||
+ }
|
||
+
|
||
+ void record_other_type(int byte_size, bool read_only) {
|
||
+ int which = (read_only) ? RO : RW;
|
||
+ _bytes [which][OtherType] += byte_size;
|
||
+ }
|
||
+
|
||
+ void print_stats(int ro_all, int rw_all);
|
||
+};
|
||
+
|
||
+#endif // SHARE_VM_CDS_DUMPALLOCSTATS_HPP
|
||
diff --git a/hotspot/src/share/vm/cds/dynamicArchive.cpp b/hotspot/src/share/vm/cds/dynamicArchive.cpp
|
||
new file mode 100644
|
||
index 000000000..efed275c8
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/cds/dynamicArchive.cpp
|
||
@@ -0,0 +1,412 @@
|
||
+/*
|
||
+ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 "cds/archiveBuilder.hpp"
|
||
+#include "cds/archiveUtils.hpp"
|
||
+#include "cds/dynamicArchive.hpp"
|
||
+#include "classfile/systemDictionaryShared.hpp"
|
||
+#include "runtime/vm_operations.hpp"
|
||
+#include "runtime/arguments.hpp"
|
||
+#include "runtime/vmThread.hpp"
|
||
+#include "memory/metaspaceShared.hpp"
|
||
+#include "memory/filemap.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
+#include "utilities/exceptions.hpp"
|
||
+#include "utilities/align.hpp"
|
||
+#include "utilities/bitMap.hpp"
|
||
+#include "utilities/exceptions.hpp"
|
||
+
|
||
+class DynamicArchiveBuilder : public ArchiveBuilder {
|
||
+public:
|
||
+ 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;
|
||
+ }
|
||
+
|
||
+ u4 a_offset = ArchiveBuilder::current()->any_to_offset_u4(a_name);
|
||
+ u4 b_offset = ArchiveBuilder::current()->any_to_offset_u4(b_name);
|
||
+
|
||
+ if (a_offset < b_offset) {
|
||
+ return -1;
|
||
+ } else {
|
||
+ assert(a_offset > b_offset, "must be");
|
||
+ return 1;
|
||
+ }
|
||
+ }
|
||
+
|
||
+public:
|
||
+ FileMapInfo::DynamicArchiveHeader* _header;
|
||
+
|
||
+ void init_header();
|
||
+ void release_header();
|
||
+ void sort_methods();
|
||
+ void sort_methods(InstanceKlass* ik) const;
|
||
+ void remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const;
|
||
+ void write_archive(char* serialized_data);
|
||
+ virtual void iterate_roots(MetaspaceClosure* it, bool is_relocating_pointers) {
|
||
+ SystemDictionaryShared::dumptime_classes_do(it);
|
||
+ }
|
||
+
|
||
+ // Do this before and after the archive dump to see if any corruption
|
||
+ // is caused by dynamic dumping.
|
||
+ void verify_universe(const char* info) {
|
||
+ if (VerifyBeforeExit) {
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Verify %s", info);
|
||
+ }
|
||
+ // Among other things, this ensures that Eden top is correct.
|
||
+ Universe::heap()->prepare_for_verify();
|
||
+ Universe::verify(info);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ void doit() {
|
||
+ SystemDictionaryShared::start_dumping();
|
||
+
|
||
+ verify_universe("Before CDS dynamic dump");
|
||
+ DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm);
|
||
+
|
||
+ // No need DumpTimeTable_lock, since jdk8 doesn't support jcmd dump.
|
||
+ // Just remains this lock.
|
||
+ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
||
+ SystemDictionaryShared::check_excluded_classes();
|
||
+ SystemDictionaryShared::replace_klass_in_constantPool();
|
||
+
|
||
+ init_header();
|
||
+ gather_source_objs();
|
||
+ if (klasses()->length() == 0) {
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("No classes gathered, so do not generate Dynamic CDS jsa");
|
||
+ }
|
||
+ return;
|
||
+ }
|
||
+ reserve_buffer();
|
||
+
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Copying %d klasses and %d symbols",
|
||
+ klasses()->length(), symbols()->length());
|
||
+ }
|
||
+ dump_rw_metadata();
|
||
+ dump_ro_metadata();
|
||
+ relocate_metaspaceobj_embedded_pointers();
|
||
+ relocate_roots();
|
||
+
|
||
+ verify_estimate_size(_estimated_metaspaceobj_bytes, "MetaspaceObjs");
|
||
+
|
||
+ char* serialized_data;
|
||
+ {
|
||
+ // Write the symbol table and system dictionaries to the RO space.
|
||
+ // Note that these tables still point to the *original* objects, so
|
||
+ // they would need to get the correct addresses.
|
||
+ assert(current_dump_space() == ro_region(), "Must be RO space");
|
||
+ SymbolTable::write_to_archive(symbols());
|
||
+
|
||
+ ArchiveBuilder::OtherROAllocMark mark;
|
||
+ SystemDictionaryShared::write_to_archive();
|
||
+
|
||
+ serialized_data = ro_region()->top();
|
||
+ DynamicWriteClosure wc(ro_region());
|
||
+ SymbolTable::serialize_shared_table_header(&wc);
|
||
+ SystemDictionaryShared::serialize_dictionary_headers(&wc);
|
||
+ }
|
||
+
|
||
+ verify_estimate_size(_estimated_hashtable_bytes, "Hashtables");
|
||
+
|
||
+ sort_methods();
|
||
+
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Make classes shareable");
|
||
+ }
|
||
+ make_klasses_shareable();
|
||
+
|
||
+ patch_shared_obj_vtable();
|
||
+
|
||
+ relocate_to_requested();
|
||
+
|
||
+ write_archive(serialized_data);
|
||
+ release_header();
|
||
+
|
||
+ assert(_num_dump_regions_used == _total_dump_regions, "must be");
|
||
+ verify_universe("After CDS dynamic dump");
|
||
+ }
|
||
+};
|
||
+
|
||
+void DynamicArchiveBuilder::init_header() {
|
||
+ FileMapInfo* mapinfo = new FileMapInfo(false);
|
||
+ assert(FileMapInfo::dynamic_info() == mapinfo, "must be");
|
||
+ _header = mapinfo->dynamic_header();
|
||
+
|
||
+ FileMapInfo* base_info = FileMapInfo::current_info();
|
||
+ _header->set_base_header_crc(base_info->header()->crc());
|
||
+ for (int i = 0; i < MetaspaceShared::n_regions; i++) {
|
||
+ _header->set_base_region_crc(i, base_info->header()->space_crc(i));
|
||
+ }
|
||
+
|
||
+ _header->populate(base_info, base_info->alignment());
|
||
+}
|
||
+
|
||
+void DynamicArchiveBuilder::release_header() {
|
||
+ // We temporarily allocated a dynamic FileMapInfo for dumping, which makes it appear we
|
||
+ // have mapped a dynamic archive, but we actually have not. We are in a safepoint now.
|
||
+ // Let's free it so that if class loading happens after we leave the safepoint, nothing
|
||
+ // bad will happen.
|
||
+ assert(SafepointSynchronize::is_at_safepoint(), "must be");
|
||
+ FileMapInfo *mapinfo = FileMapInfo::dynamic_info();
|
||
+ assert(mapinfo != NULL && _header == mapinfo->dynamic_header(), "must be");
|
||
+ delete mapinfo;
|
||
+ assert(!DynamicArchive::is_mapped(), "must be");
|
||
+ _header = NULL;
|
||
+}
|
||
+
|
||
+void DynamicArchiveBuilder::sort_methods() {
|
||
+ // Because high version support jcmd dynamic cds dump, jvm need go on after dump.
|
||
+ // Jdk8 no need as so, just exit after dump.
|
||
+ InstanceKlass::disable_method_binary_search();
|
||
+ for (int i = 0; i < klasses()->length(); i++) {
|
||
+ Klass* k = klasses()->at(i);
|
||
+ if (k->oop_is_instance()) {
|
||
+ sort_methods(InstanceKlass::cast(k));
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+// The address order of the copied Symbols may be different than when the original
|
||
+// klasses were created. Re-sort all the tables. See Method::sort_methods().
|
||
+void DynamicArchiveBuilder::sort_methods(InstanceKlass* ik) const {
|
||
+ assert(ik != NULL, "DynamicArchiveBuilder currently doesn't support dumping the base archive");
|
||
+ if (MetaspaceShared::is_in_shared_space(ik)) {
|
||
+ // We have reached a supertype that's already in the base archive
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ if (ik->java_mirror() == NULL) {
|
||
+ // NULL mirror means this class has already been visited and methods are already sorted
|
||
+ return;
|
||
+ }
|
||
+ ik->remove_java_mirror();
|
||
+
|
||
+ if (DebugDynamicCDS) {
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("sorting methods for " PTR_FORMAT " (" PTR_FORMAT ") %s",
|
||
+ p2i(ik), p2i(to_requested(ik)), ik->external_name());
|
||
+ }
|
||
+ // Method sorting may re-layout the [iv]tables, which would change the offset(s)
|
||
+ // of the locations in an InstanceKlass that would contain pointers. Let's clear
|
||
+ // all the existing pointer marking bits, and re-mark the pointers after sorting.
|
||
+ remark_pointers_for_instance_klass(ik, false);
|
||
+
|
||
+ // Make sure all supertypes have been sorted
|
||
+ sort_methods(ik->java_super());
|
||
+ Array<Klass*>* interfaces = ik->local_interfaces();
|
||
+ int len = interfaces->length();
|
||
+ for (int i = 0; i < len; i++) {
|
||
+ sort_methods(InstanceKlass::cast(interfaces->at(i)));
|
||
+ }
|
||
+
|
||
+#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_space(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_space(name) || is_in_buffer_space(name), "must be");
|
||
+ }
|
||
+ }
|
||
+#endif
|
||
+
|
||
+ Method::sort_methods(ik->methods(), /*idempotent=*/false, /*set_idnums=*/true, dynamic_dump_method_comparator);
|
||
+ if (ik->default_methods() != NULL) {
|
||
+ Method::sort_methods(ik->default_methods(), /*idempotent=*/false, /*set_idnums=*/false, dynamic_dump_method_comparator);
|
||
+ }
|
||
+
|
||
+ EXCEPTION_MARK;
|
||
+
|
||
+ ik->vtable()->initialize_vtable(false, CATCH); // No need checkconstraints
|
||
+ CLEAR_PENDING_EXCEPTION;
|
||
+ ik->itable()->initialize_itable(false, CATCH);
|
||
+ CLEAR_PENDING_EXCEPTION;
|
||
+
|
||
+ // Set all the pointer marking bits after sorting.
|
||
+ remark_pointers_for_instance_klass(ik, true);
|
||
+}
|
||
+
|
||
+template<bool should_mark>
|
||
+class PointerRemarker: public MetaspaceClosure {
|
||
+public:
|
||
+ virtual bool do_ref(Ref* ref, bool read_only) {
|
||
+ if (should_mark) {
|
||
+ ArchivePtrMarker::mark_pointer(ref->addr());
|
||
+ } else {
|
||
+ ArchivePtrMarker::clear_pointer(ref->addr());
|
||
+ }
|
||
+ return false; // don't recurse
|
||
+ }
|
||
+};
|
||
+
|
||
+void DynamicArchiveBuilder::remark_pointers_for_instance_klass(InstanceKlass* k, bool should_mark) const {
|
||
+ if (should_mark) {
|
||
+ PointerRemarker<true> marker;
|
||
+ k->metaspace_pointers_do(&marker);
|
||
+ marker.finish();
|
||
+ } else {
|
||
+ PointerRemarker<false> marker;
|
||
+ k->metaspace_pointers_do(&marker);
|
||
+ marker.finish();
|
||
+ }
|
||
+}
|
||
+
|
||
+void DynamicArchiveBuilder::write_archive(char* serialized_data) {
|
||
+ _header->set_serialized_data(serialized_data);
|
||
+
|
||
+ FileMapInfo* dynamic_info = FileMapInfo::dynamic_info();
|
||
+ assert(dynamic_info != NULL, "Sanity");
|
||
+
|
||
+ // Update file offset
|
||
+ ArchiveBuilder::write_archive(dynamic_info);
|
||
+
|
||
+ // Write into file
|
||
+ dynamic_info->open_for_write();
|
||
+ dynamic_info->set_requested_base((char*)MetaspaceShared::requested_base_address());
|
||
+ dynamic_info->set_header_base_archive_name_size(strlen(Arguments::GetSharedArchivePath()) + 1);
|
||
+ dynamic_info->set_header_crc(dynamic_info->compute_header_crc());
|
||
+ ArchiveBuilder::write_archive(dynamic_info);
|
||
+
|
||
+ address base = _requested_dynamic_archive_bottom;
|
||
+ address top = _requested_dynamic_archive_top;
|
||
+ size_t file_size = pointer_delta(top, base, sizeof(char));
|
||
+
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Written dynamic archive " PTR_FORMAT " - " PTR_FORMAT
|
||
+ " , " SIZE_FORMAT " bytes total]",
|
||
+ p2i(base), p2i(top), file_size);
|
||
+
|
||
+ dynamic_cds_log->print_cr("%d klasses; %d symbols", klasses()->length(), symbols()->length());
|
||
+ }
|
||
+}
|
||
+
|
||
+class VM_GC_Sync_Operation : public VM_Operation {
|
||
+public:
|
||
+
|
||
+ VM_GC_Sync_Operation() : VM_Operation() { }
|
||
+
|
||
+ // Acquires the Heap_lock.
|
||
+ virtual bool doit_prologue() {
|
||
+ Heap_lock->lock();
|
||
+ return true;
|
||
+ }
|
||
+ // Releases the Heap_lock.
|
||
+ virtual void doit_epilogue() {
|
||
+ Heap_lock->unlock();
|
||
+ }
|
||
+};
|
||
+
|
||
+class VM_PopulateDynamicDumpSharedSpace : public VM_GC_Sync_Operation {
|
||
+ DynamicArchiveBuilder builder;
|
||
+public:
|
||
+ VM_PopulateDynamicDumpSharedSpace() : VM_GC_Sync_Operation() {}
|
||
+ VMOp_Type type() const { return VMOp_PopulateDumpSharedSpace; }
|
||
+ void doit() {
|
||
+ if (DynamicDumpSharedSpaces == false) {
|
||
+ return;
|
||
+ }
|
||
+ ResourceMark rm;
|
||
+
|
||
+ if (SystemDictionaryShared::empty_dumptime_table()) {
|
||
+ tty->print_cr("There is no class to be included in the dynamic archive.");
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ builder.doit();
|
||
+
|
||
+ DynamicDumpSharedSpaces = false;
|
||
+ exit(0);
|
||
+ }
|
||
+};
|
||
+
|
||
+bool DynamicArchive::_has_been_dumped_once = false;
|
||
+
|
||
+void DynamicArchive::prepare_for_dynamic_dumping_at_exit() {
|
||
+ {
|
||
+ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
||
+ if (DynamicArchive::has_been_dumped_once()) {
|
||
+ return;
|
||
+ } else {
|
||
+ DynamicArchive::set_has_been_dumped_once();
|
||
+ }
|
||
+ }
|
||
+ EXCEPTION_MARK;
|
||
+ ResourceMark rm(THREAD);
|
||
+ MetaspaceShared::link_and_cleanup_shared_classes(THREAD);
|
||
+
|
||
+ if (HAS_PENDING_EXCEPTION) {
|
||
+ tty->print_cr("ArchiveClassesAtExit has failed");
|
||
+ tty->print_cr("%s: %s", PENDING_EXCEPTION->klass()->external_name(),
|
||
+ java_lang_String::as_utf8_string(java_lang_Throwable::message(PENDING_EXCEPTION)));
|
||
+ // We cannot continue to dump the archive anymore.
|
||
+ DynamicDumpSharedSpaces = false;
|
||
+ CLEAR_PENDING_EXCEPTION;
|
||
+ }
|
||
+}
|
||
+
|
||
+void DynamicArchive::dump() {
|
||
+ if (Arguments::GetSharedDynamicArchivePath() == NULL) {
|
||
+ tty->print_cr("SharedDynamicArchivePath is not specified");
|
||
+ return;
|
||
+ }
|
||
+
|
||
+ VM_PopulateDynamicDumpSharedSpace op;
|
||
+ VMThread::execute(&op);
|
||
+}
|
||
+
|
||
+bool DynamicArchive::validate(FileMapInfo* dynamic_info) {
|
||
+ assert(!dynamic_info->is_static(), "must be");
|
||
+ // Check if the recorded base archive matches with the current one
|
||
+ FileMapInfo* base_info = FileMapInfo::current_info();
|
||
+ FileMapInfo::DynamicArchiveHeader* dynamic_header = dynamic_info->dynamic_header();
|
||
+
|
||
+ // Check the header crc
|
||
+ if (dynamic_header->base_header_crc() != base_info->crc()) {
|
||
+ FileMapInfo::fail_continue("Dynamic archive cannot be used: static archive header checksum verification failed.");
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Check each space's crc
|
||
+ for (int i = 0; i < MetaspaceShared::n_regions; i++) {
|
||
+ if (dynamic_header->base_region_crc(i) != base_info->space_crc(i)) {
|
||
+ FileMapInfo::fail_continue("Dynamic archive cannot be used: static archive region #%d checksum verification failed.", i);
|
||
+ return false;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return true;
|
||
+}
|
||
diff --git a/hotspot/src/share/vm/cds/dynamicArchive.hpp b/hotspot/src/share/vm/cds/dynamicArchive.hpp
|
||
new file mode 100644
|
||
index 000000000..1d5b71221
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/cds/dynamicArchive.hpp
|
||
@@ -0,0 +1,54 @@
|
||
+/*
|
||
+ * Copyright (c) 2019, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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_VM_CDS_DYNAMICARCHIVE_HPP
|
||
+#define SHARE_VM_CDS_DYNAMICARCHIVE_HPP
|
||
+
|
||
+//#include "classfile/compactHashtable.hpp"
|
||
+#include "memory/allocation.hpp"
|
||
+#include "memory/filemap.hpp"
|
||
+#include "memory/memRegion.hpp"
|
||
+#include "runtime/virtualspace.hpp"
|
||
+#include "oops/oop.hpp"
|
||
+#include "utilities/exceptions.hpp"
|
||
+#include "utilities/macros.hpp"
|
||
+#include "utilities/resourceHash.hpp"
|
||
+
|
||
+#if INCLUDE_CDS
|
||
+
|
||
+// Fixme
|
||
+class DynamicArchive : AllStatic {
|
||
+ static bool _has_been_dumped_once;
|
||
+public:
|
||
+ static void prepare_for_dynamic_dumping_at_exit();
|
||
+ static void dump();
|
||
+ static bool has_been_dumped_once() { return _has_been_dumped_once; }
|
||
+ static void set_has_been_dumped_once() { _has_been_dumped_once = true; }
|
||
+ static bool is_mapped() { return FileMapInfo::dynamic_info() != NULL; }
|
||
+ static bool validate(FileMapInfo* dynamic_info);
|
||
+};
|
||
+
|
||
+#endif // INCLUDE_CDS
|
||
+#endif // SHARE_VM_CDS_DYNAMICARCHIVE_HPP
|
||
diff --git a/hotspot/src/share/vm/classfile/classFileParser.cpp b/hotspot/src/share/vm/classfile/classFileParser.cpp
|
||
index 5c36a9d6f..ae9199525 100644
|
||
--- a/hotspot/src/share/vm/classfile/classFileParser.cpp
|
||
+++ b/hotspot/src/share/vm/classfile/classFileParser.cpp
|
||
@@ -4376,6 +4376,13 @@ instanceKlassHandle ClassFileParser::parseClassFile(Symbol* name,
|
||
instanceKlassHandle this_klass (THREAD, preserve_this_klass);
|
||
debug_only(this_klass->verify();)
|
||
|
||
+#if INCLUDE_CDS
|
||
+ if (DynamicDumpSharedSpaces && !SystemDictionary::is_builtin_loader(class_loader)) {
|
||
+ this_klass->set_shared_classpath_index(UNREGISTERED_INDEX);
|
||
+ SystemDictionaryShared::set_shared_class_misc_info(this_klass(), cfs);
|
||
+ }
|
||
+#endif // INCLUDE_CDS
|
||
+
|
||
// Clear class if no error has occurred so destructor doesn't deallocate it
|
||
_klass = NULL;
|
||
return this_klass;
|
||
diff --git a/hotspot/src/share/vm/classfile/classLoaderExt.hpp b/hotspot/src/share/vm/classfile/classLoaderExt.hpp
|
||
index 7b2360af9..3bd4f3bde 100644
|
||
--- a/hotspot/src/share/vm/classfile/classLoaderExt.hpp
|
||
+++ b/hotspot/src/share/vm/classfile/classLoaderExt.hpp
|
||
@@ -48,7 +48,7 @@ public:
|
||
instanceKlassHandle record_result(const int classpath_index,
|
||
ClassPathEntry* e, instanceKlassHandle result, TRAPS) {
|
||
if (ClassLoader::add_package(_file_name, classpath_index, THREAD)) {
|
||
- if (DumpSharedSpaces) {
|
||
+ if (DumpSharedSpaces || DynamicDumpSharedSpaces) {
|
||
result->set_shared_classpath_index(classpath_index);
|
||
}
|
||
return result;
|
||
diff --git a/hotspot/src/share/vm/classfile/compactHashtable.cpp b/hotspot/src/share/vm/classfile/compactHashtable.cpp
|
||
new file mode 100644
|
||
index 000000000..232a89fa1
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/classfile/compactHashtable.cpp
|
||
@@ -0,0 +1,216 @@
|
||
+/*
|
||
+ * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 "jvm.h"
|
||
+#include "cds/archiveBuilder.hpp"
|
||
+#include "classfile/compactHashtable.hpp"
|
||
+#include "classfile/javaClasses.hpp"
|
||
+#include "memory/metadataFactory.hpp"
|
||
+#include "runtime/arguments.hpp"
|
||
+#include "runtime/globals.hpp"
|
||
+#include "runtime/vmThread.hpp"
|
||
+#include "utilities/align.hpp"
|
||
+#include "utilities/numberSeq.hpp"
|
||
+
|
||
+/////////////////////////////////////////////////////
|
||
+//
|
||
+// The compact hash table writer implementations
|
||
+//
|
||
+CompactHashtableWriter::CompactHashtableWriter(int num_entries,
|
||
+ CompactHashtableStats* stats) {
|
||
+ Arguments::assert_is_dumping_archive();
|
||
+ assert(num_entries >= 0, "sanity");
|
||
+ _num_buckets = calculate_num_buckets(num_entries);
|
||
+ assert(_num_buckets > 0, "no buckets");
|
||
+
|
||
+ _num_entries_written = 0;
|
||
+ _buckets = NEW_C_HEAP_ARRAY(GrowableArray<Entry>*, _num_buckets, mtSymbol);
|
||
+ for (int i = 0; i < _num_buckets; i++) {
|
||
+ _buckets[i] = new (ResourceObj::C_HEAP, mtSymbol) GrowableArray<Entry>(0, true, mtSymbol);
|
||
+ }
|
||
+
|
||
+ _stats = stats;
|
||
+ _compact_buckets = NULL;
|
||
+ _compact_entries = NULL;
|
||
+ _num_empty_buckets = 0;
|
||
+ _num_value_only_buckets = 0;
|
||
+ _num_other_buckets = 0;
|
||
+}
|
||
+
|
||
+CompactHashtableWriter::~CompactHashtableWriter() {
|
||
+ for (int index = 0; index < _num_buckets; index++) {
|
||
+ GrowableArray<Entry>* bucket = _buckets[index];
|
||
+ delete bucket;
|
||
+ }
|
||
+
|
||
+ FREE_C_HEAP_ARRAY(GrowableArray<Entry>*, _buckets, mtSymbol);
|
||
+}
|
||
+
|
||
+size_t CompactHashtableWriter::estimate_size(int num_entries) {
|
||
+ int num_buckets = calculate_num_buckets(num_entries);
|
||
+ size_t bucket_bytes = ArchiveBuilder::ro_array_bytesize<u4>(num_buckets + 1);
|
||
+
|
||
+ // In worst case, we have no VALUE_ONLY_BUCKET_TYPE, so each entry takes 2 slots
|
||
+ int entries_space = 2 * num_entries;
|
||
+ size_t entry_bytes = ArchiveBuilder::ro_array_bytesize<u4>(entries_space);
|
||
+
|
||
+ return bucket_bytes
|
||
+ + entry_bytes
|
||
+ + SimpleCompactHashtable::calculate_header_size();
|
||
+}
|
||
+
|
||
+// Add a symbol entry to the temporary hash table
|
||
+void CompactHashtableWriter::add(unsigned int hash, u4 value) {
|
||
+ int index = hash % _num_buckets;
|
||
+ _buckets[index]->append_if_missing(Entry(hash, value));
|
||
+ _num_entries_written++;
|
||
+}
|
||
+
|
||
+void CompactHashtableWriter::allocate_table() {
|
||
+ int entries_space = 0;
|
||
+ for (int index = 0; index < _num_buckets; index++) {
|
||
+ GrowableArray<Entry>* bucket = _buckets[index];
|
||
+ int bucket_size = bucket->length();
|
||
+ if (bucket_size == 1) {
|
||
+ entries_space++;
|
||
+ } else if (bucket_size > 1) {
|
||
+ entries_space += 2 * bucket_size;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (entries_space & ~BUCKET_OFFSET_MASK) {
|
||
+ vm_exit_during_initialization("CompactHashtableWriter::allocate_table: Overflow! "
|
||
+ "Too many entries.");
|
||
+ }
|
||
+
|
||
+ _compact_buckets = ArchiveBuilder::new_ro_array<u4>(_num_buckets + 1);
|
||
+ _compact_entries = ArchiveBuilder::new_ro_array<u4>(entries_space);
|
||
+
|
||
+ _stats->bucket_count = _num_buckets;
|
||
+ _stats->bucket_bytes = align_up(_compact_buckets->size() * BytesPerWord,
|
||
+ KlassAlignmentInBytes);
|
||
+ _stats->hashentry_count = _num_entries_written;
|
||
+ _stats->hashentry_bytes = align_up(_compact_entries->size() * BytesPerWord,
|
||
+ KlassAlignmentInBytes);
|
||
+}
|
||
+
|
||
+// Write the compact table's buckets
|
||
+void CompactHashtableWriter::dump_table(NumberSeq* summary) {
|
||
+ u4 offset = 0;
|
||
+ for (int index = 0; index < _num_buckets; index++) {
|
||
+ GrowableArray<Entry>* bucket = _buckets[index];
|
||
+ int bucket_size = bucket->length();
|
||
+ if (bucket_size == 1) {
|
||
+ // bucket with one entry is compacted and only has the symbol offset
|
||
+ _compact_buckets->at_put(index, BUCKET_INFO(offset, VALUE_ONLY_BUCKET_TYPE));
|
||
+
|
||
+ Entry ent = bucket->at(0);
|
||
+ _compact_entries->at_put(offset++, ent.value());
|
||
+ _num_value_only_buckets++;
|
||
+ } else {
|
||
+ // regular bucket, each entry is a symbol (hash, offset) pair
|
||
+ _compact_buckets->at_put(index, BUCKET_INFO(offset, REGULAR_BUCKET_TYPE));
|
||
+
|
||
+ for (int i=0; i<bucket_size; i++) {
|
||
+ Entry ent = bucket->at(i);
|
||
+ _compact_entries->at_put(offset++, u4(ent.hash())); // write entry hash
|
||
+ _compact_entries->at_put(offset++, ent.value());
|
||
+ }
|
||
+ if (bucket_size == 0) {
|
||
+ _num_empty_buckets++;
|
||
+ } else {
|
||
+ _num_other_buckets++;
|
||
+ }
|
||
+ }
|
||
+ summary->add(bucket_size);
|
||
+ }
|
||
+
|
||
+ // Mark the end of the buckets
|
||
+ _compact_buckets->at_put(_num_buckets, BUCKET_INFO(offset, TABLEEND_BUCKET_TYPE));
|
||
+ assert(offset == (u4)_compact_entries->length(), "sanity");
|
||
+}
|
||
+
|
||
+// Write the compact table
|
||
+void CompactHashtableWriter::dump(SimpleCompactHashtable *cht, const char* table_name) {
|
||
+ NumberSeq summary;
|
||
+ allocate_table();
|
||
+ dump_table(&summary);
|
||
+
|
||
+ int table_bytes = _stats->bucket_bytes + _stats->hashentry_bytes;
|
||
+ address base_address = address(SharedBaseAddress);
|
||
+ cht->init(base_address, _num_entries_written, _num_buckets,
|
||
+ _compact_buckets->data(), _compact_entries->data());
|
||
+
|
||
+ if (InfoDynamicCDS) {
|
||
+ double avg_cost = 0.0;
|
||
+ if (_num_entries_written > 0) {
|
||
+ avg_cost = double(table_bytes)/double(_num_entries_written);
|
||
+ }
|
||
+ dynamic_cds_log->print_cr("Shared %s table stats -------- base: " PTR_FORMAT,
|
||
+ table_name, (intptr_t)base_address);
|
||
+ dynamic_cds_log->print_cr("Number of entries : %9d", _num_entries_written);
|
||
+ dynamic_cds_log->print_cr("Total bytes used : %9d", table_bytes);
|
||
+ dynamic_cds_log->print_cr("Average bytes per entry : %9.3f", avg_cost);
|
||
+ dynamic_cds_log->print_cr("Average bucket size : %9.3f", summary.avg());
|
||
+ dynamic_cds_log->print_cr("Variance of bucket size : %9.3f", summary.variance());
|
||
+ dynamic_cds_log->print_cr("Std. dev. of bucket size: %9.3f", summary.sd());
|
||
+ dynamic_cds_log->print_cr("Maximum bucket size : %9d", (int)summary.maximum());
|
||
+ dynamic_cds_log->print_cr("Empty buckets : %9d", _num_empty_buckets);
|
||
+ dynamic_cds_log->print_cr("Value_Only buckets : %9d", _num_value_only_buckets);
|
||
+ dynamic_cds_log->print_cr("Other buckets : %9d", _num_other_buckets);
|
||
+ }
|
||
+}
|
||
+
|
||
+/////////////////////////////////////////////////////////////
|
||
+//
|
||
+// The CompactHashtable implementation
|
||
+//
|
||
+
|
||
+void SimpleCompactHashtable::init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries) {
|
||
+ _bucket_count = bucket_count;
|
||
+ _entry_count = entry_count;
|
||
+ _base_address = base_address;
|
||
+ _buckets = buckets;
|
||
+ _entries = entries;
|
||
+}
|
||
+
|
||
+size_t SimpleCompactHashtable::calculate_header_size() {
|
||
+ // We have 5 fields. Each takes up sizeof(intptr_t). See WriteClosure::do_u4
|
||
+ size_t bytes = sizeof(intptr_t) * 5;
|
||
+ return bytes;
|
||
+}
|
||
+
|
||
+void SimpleCompactHashtable::serialize_header(SerializeClosure* soc) {
|
||
+ // NOTE: if you change this function, you MUST change the number 5 in
|
||
+ // calculate_header_size() accordingly.
|
||
+ soc->do_u4(&_entry_count);
|
||
+ soc->do_u4(&_bucket_count);
|
||
+ soc->do_ptr((void**)&_buckets);
|
||
+ soc->do_ptr((void**)&_entries);
|
||
+ if (soc->reading()) {
|
||
+ _base_address = (address)SharedBaseAddress;
|
||
+ }
|
||
+}
|
||
diff --git a/hotspot/src/share/vm/classfile/compactHashtable.hpp b/hotspot/src/share/vm/classfile/compactHashtable.hpp
|
||
new file mode 100644
|
||
index 000000000..727b3ebfb
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/classfile/compactHashtable.hpp
|
||
@@ -0,0 +1,349 @@
|
||
+/*
|
||
+ * Copyright (c) 1997, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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_VM_CLASSFILE_COMPACTHASHTABLE_HPP
|
||
+#define SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP
|
||
+
|
||
+#include "oops/symbol.hpp"
|
||
+#include "runtime/globals.hpp"
|
||
+#include "utilities/array.hpp"
|
||
+#include "utilities/growableArray.hpp"
|
||
+#include "utilities/numberSeq.hpp"
|
||
+
|
||
+
|
||
+template <
|
||
+ typename K,
|
||
+ typename V,
|
||
+ V (*DECODE)(address base_address, u4 offset),
|
||
+ bool (*EQUALS)(V value, K key, int len)
|
||
+ >
|
||
+class CompactHashtable;
|
||
+class NumberSeq;
|
||
+class SimpleCompactHashtable;
|
||
+
|
||
+// Stats for symbol tables in the CDS archive
|
||
+class CompactHashtableStats {
|
||
+public:
|
||
+ int hashentry_count;
|
||
+ int hashentry_bytes;
|
||
+ int bucket_count;
|
||
+ int bucket_bytes;
|
||
+
|
||
+ CompactHashtableStats() :
|
||
+ hashentry_count(0), hashentry_bytes(0),
|
||
+ bucket_count(0), bucket_bytes(0) {}
|
||
+};
|
||
+
|
||
+/////////////////////////////////////////////////////////////////////////
|
||
+//
|
||
+// The compact hash table writer. Used at dump time for writing out
|
||
+// the compact table to the shared archive.
|
||
+//
|
||
+// At dump time, the CompactHashtableWriter obtains all entries from the
|
||
+// symbol/string table and adds them to a new temporary hash table. The hash
|
||
+// table size (number of buckets) is calculated using
|
||
+// '(num_entries + bucket_size - 1) / bucket_size'. The default bucket
|
||
+// size is 4 and can be changed by -XX:SharedSymbolTableBucketSize option.
|
||
+// 4 is chosen because it produces smaller sized bucket on average for
|
||
+// faster lookup. It also has relatively small number of empty buckets and
|
||
+// good distribution of the entries.
|
||
+//
|
||
+// We use a simple hash function (hash % num_bucket) for the table.
|
||
+// The new table is compacted when written out. Please see comments
|
||
+// above the CompactHashtable class for the table layout detail. The bucket
|
||
+// offsets are written to the archive as part of the compact table. The
|
||
+// bucket offset is encoded in the low 30-bit (0-29) and the bucket type
|
||
+// (regular or compact) are encoded in bit[31, 30]. For buckets with more
|
||
+// than one entry, both hash and entry offset are written to the
|
||
+// table. For buckets with only one entry, only the entry offset is written
|
||
+// to the table and the buckets are tagged as compact in their type bits.
|
||
+// Buckets without entry are skipped from the table. Their offsets are
|
||
+// still written out for faster lookup.
|
||
+//
|
||
+class CompactHashtableWriter: public StackObj {
|
||
+public:
|
||
+ class Entry {
|
||
+ unsigned int _hash;
|
||
+ u4 _value;
|
||
+
|
||
+ public:
|
||
+ Entry() {}
|
||
+ Entry(unsigned int hash, u4 val) : _hash(hash), _value(val) {}
|
||
+
|
||
+ u4 value() {
|
||
+ return _value;
|
||
+ }
|
||
+ unsigned int hash() {
|
||
+ return _hash;
|
||
+ }
|
||
+
|
||
+ bool operator==(const CompactHashtableWriter::Entry& other) {
|
||
+ return (_value == other._value && _hash == other._hash);
|
||
+ }
|
||
+ }; // class CompactHashtableWriter::Entry
|
||
+
|
||
+private:
|
||
+ int _num_entries_written;
|
||
+ int _num_buckets;
|
||
+ int _num_empty_buckets;
|
||
+ int _num_value_only_buckets;
|
||
+ int _num_other_buckets;
|
||
+ GrowableArray<Entry>** _buckets;
|
||
+ CompactHashtableStats* _stats;
|
||
+ Array<u4>* _compact_buckets;
|
||
+ Array<u4>* _compact_entries;
|
||
+
|
||
+public:
|
||
+ // This is called at dump-time only
|
||
+ CompactHashtableWriter(int num_entries, CompactHashtableStats* stats);
|
||
+ ~CompactHashtableWriter();
|
||
+
|
||
+ void add(unsigned int hash, u4 value);
|
||
+
|
||
+private:
|
||
+ void allocate_table();
|
||
+ void dump_table(NumberSeq* summary);
|
||
+
|
||
+ static int calculate_num_buckets(int num_entries) {
|
||
+ int num_buckets = num_entries / SharedSymbolTableBucketSize;
|
||
+ // calculation of num_buckets can result in zero buckets, we need at least one
|
||
+ return (num_buckets < 1) ? 1 : num_buckets;
|
||
+ }
|
||
+
|
||
+public:
|
||
+ void dump(SimpleCompactHashtable *cht, const char* table_name);
|
||
+
|
||
+ static size_t estimate_size(int num_entries);
|
||
+};
|
||
+
|
||
+#define REGULAR_BUCKET_TYPE 0
|
||
+#define VALUE_ONLY_BUCKET_TYPE 1
|
||
+#define TABLEEND_BUCKET_TYPE 3
|
||
+#define BUCKET_OFFSET_MASK 0x3FFFFFFF
|
||
+#define BUCKET_OFFSET(info) ((info) & BUCKET_OFFSET_MASK)
|
||
+#define BUCKET_TYPE_SHIFT 30
|
||
+#define BUCKET_TYPE(info) (((info) & ~BUCKET_OFFSET_MASK) >> BUCKET_TYPE_SHIFT)
|
||
+#define BUCKET_INFO(offset, type) (((type) << BUCKET_TYPE_SHIFT) | ((offset) & BUCKET_OFFSET_MASK))
|
||
+
|
||
+/////////////////////////////////////////////////////////////////////////////
|
||
+//
|
||
+// CompactHashtable is used to store the CDS archive's symbol/string tables.
|
||
+//
|
||
+// Because these tables are read-only (no entries can be added/deleted) at run-time
|
||
+// and tend to have large number of entries, we try to minimize the footprint
|
||
+// cost per entry.
|
||
+//
|
||
+// The CompactHashtable is split into two arrays
|
||
+//
|
||
+// u4 buckets[num_buckets+1]; // bit[31,30]: type; bit[29-0]: offset
|
||
+// u4 entries[<variable size>]
|
||
+//
|
||
+// The size of buckets[] is 'num_buckets + 1'. Each entry of
|
||
+// buckets[] is a 32-bit encoding of the bucket type and bucket offset,
|
||
+// with the type in the left-most 2-bit and offset in the remaining 30-bit.
|
||
+// The last entry is a special type. It contains the end of the last
|
||
+// bucket.
|
||
+//
|
||
+// There are two types of buckets, regular buckets and value_only buckets. The
|
||
+// value_only buckets have '01' in their highest 2-bit, and regular buckets have
|
||
+// '00' in their highest 2-bit.
|
||
+//
|
||
+// For normal buckets, each entry is 8 bytes in the entries[]:
|
||
+// u4 hash; /* symbol/string hash */
|
||
+// union {
|
||
+// u4 offset; /* Symbol* sym = (Symbol*)(base_address + offset) */
|
||
+// narrowOop str; /* String narrowOop encoding */
|
||
+// }
|
||
+//
|
||
+//
|
||
+// For value_only buckets, each entry has only the 4-byte 'offset' in the entries[].
|
||
+//
|
||
+// Example -- note that the second bucket is a VALUE_ONLY_BUCKET_TYPE so the hash code
|
||
+// is skipped.
|
||
+// buckets[0, 4, 5, ....]
|
||
+// | | |
|
||
+// | | +---+
|
||
+// | | |
|
||
+// | +----+ |
|
||
+// v v v
|
||
+// entries[H,O,H,O,O,H,O,H,O.....]
|
||
+//
|
||
+// See CompactHashtable::lookup() for how the table is searched at runtime.
|
||
+// See CompactHashtableWriter::dump() for how the table is written at CDS
|
||
+// dump time.
|
||
+//
|
||
+class SimpleCompactHashtable {
|
||
+protected:
|
||
+ address _base_address;
|
||
+ u4 _bucket_count;
|
||
+ u4 _entry_count;
|
||
+ u4* _buckets;
|
||
+ u4* _entries;
|
||
+
|
||
+public:
|
||
+ SimpleCompactHashtable() {
|
||
+ _entry_count = 0;
|
||
+ _bucket_count = 0;
|
||
+ _buckets = 0;
|
||
+ _entries = 0;
|
||
+ }
|
||
+
|
||
+ void reset() {
|
||
+ _bucket_count = 0;
|
||
+ _entry_count = 0;
|
||
+ _buckets = 0;
|
||
+ _entries = 0;
|
||
+ }
|
||
+
|
||
+ void init(address base_address, u4 entry_count, u4 bucket_count, u4* buckets, u4* entries);
|
||
+
|
||
+ // Read/Write the table's header from/to the CDS archive
|
||
+ void serialize_header(SerializeClosure* soc) NOT_CDS_RETURN;
|
||
+
|
||
+ inline bool empty() const {
|
||
+ return (_entry_count == 0);
|
||
+ }
|
||
+
|
||
+ inline size_t entry_count() const {
|
||
+ return _entry_count;
|
||
+ }
|
||
+
|
||
+ static size_t calculate_header_size();
|
||
+};
|
||
+
|
||
+template <
|
||
+ typename K,
|
||
+ typename V,
|
||
+ V (*DECODE)(address base_address, u4 offset),
|
||
+ bool (*EQUALS)(V value, K key, int len)
|
||
+ >
|
||
+class CompactHashtable : public SimpleCompactHashtable {
|
||
+ friend class VMStructs;
|
||
+
|
||
+ V decode(u4 offset) const {
|
||
+ return DECODE(_base_address, offset);
|
||
+ }
|
||
+
|
||
+public:
|
||
+ // Lookup a value V from the compact table using key K
|
||
+ inline V lookup(K key, unsigned int hash, int len) const {
|
||
+ if (_entry_count > 0) {
|
||
+ int index = hash % _bucket_count;
|
||
+ u4 bucket_info = _buckets[index];
|
||
+ u4 bucket_offset = BUCKET_OFFSET(bucket_info);
|
||
+ int bucket_type = BUCKET_TYPE(bucket_info);
|
||
+ u4* entry = _entries + bucket_offset;
|
||
+
|
||
+ if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
|
||
+ V value = decode(entry[0]);
|
||
+ if (EQUALS(value, key, len)) {
|
||
+ return value;
|
||
+ }
|
||
+ } else {
|
||
+ // This is a regular bucket, which has more than one
|
||
+ // entries. Each entry is a pair of entry (hash, offset).
|
||
+ // Seek until the end of the bucket.
|
||
+ u4* entry_max = _entries + BUCKET_OFFSET(_buckets[index + 1]);
|
||
+ while (entry < entry_max) {
|
||
+ unsigned int h = (unsigned int)(entry[0]);
|
||
+ if (h == hash) {
|
||
+ V value = decode(entry[1]);
|
||
+ if (EQUALS(value, key, len)) {
|
||
+ return value;
|
||
+ }
|
||
+ }
|
||
+ entry += 2;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ template <class ITER>
|
||
+ inline void iterate(ITER* iter) const {
|
||
+ for (u4 i = 0; i < _bucket_count; i++) {
|
||
+ u4 bucket_info = _buckets[i];
|
||
+ u4 bucket_offset = BUCKET_OFFSET(bucket_info);
|
||
+ int bucket_type = BUCKET_TYPE(bucket_info);
|
||
+ u4* entry = _entries + bucket_offset;
|
||
+
|
||
+ if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
|
||
+ iter->do_value(decode(entry[0]));
|
||
+ } else {
|
||
+ u4*entry_max = _entries + BUCKET_OFFSET(_buckets[i + 1]);
|
||
+ while (entry < entry_max) {
|
||
+ iter->do_value(decode(entry[1]));
|
||
+ entry += 2;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ void print_table_statistics(outputStream* st, const char* name) {
|
||
+ st->print_cr("%s statistics:", name);
|
||
+ int total_entries = 0;
|
||
+ int max_bucket = 0;
|
||
+ for (u4 i = 0; i < _bucket_count; i++) {
|
||
+ u4 bucket_info = _buckets[i];
|
||
+ int bucket_type = BUCKET_TYPE(bucket_info);
|
||
+ int bucket_size;
|
||
+
|
||
+ if (bucket_type == VALUE_ONLY_BUCKET_TYPE) {
|
||
+ bucket_size = 1;
|
||
+ } else {
|
||
+ bucket_size = (BUCKET_OFFSET(_buckets[i + 1]) - BUCKET_OFFSET(bucket_info)) / 2;
|
||
+ }
|
||
+ total_entries += bucket_size;
|
||
+ if (max_bucket < bucket_size) {
|
||
+ max_bucket = bucket_size;
|
||
+ }
|
||
+ }
|
||
+ st->print_cr("Number of buckets : %9d", _bucket_count);
|
||
+ st->print_cr("Number of entries : %9d", total_entries);
|
||
+ st->print_cr("Maximum bucket size : %9d", max_bucket);
|
||
+ }
|
||
+};
|
||
+
|
||
+////////////////////////////////////////////////////////////////////////
|
||
+//
|
||
+// OffsetCompactHashtable -- This is used to store many types of objects
|
||
+// in the CDS archive. On 64-bit platforms, we save space by using a 32-bit
|
||
+// offset from the CDS base address.
|
||
+
|
||
+template <typename V>
|
||
+inline V read_value_from_compact_hashtable(address base_address, u4 offset) {
|
||
+ return (V)(base_address + offset);
|
||
+}
|
||
+
|
||
+template <
|
||
+ typename K,
|
||
+ typename V,
|
||
+ bool (*EQUALS)(V value, K key, int len)
|
||
+ >
|
||
+class OffsetCompactHashtable : public CompactHashtable<
|
||
+ K, V, read_value_from_compact_hashtable<V>, EQUALS> {
|
||
+};
|
||
+
|
||
+#endif // SHARE_VM_CLASSFILE_COMPACTHASHTABLE_HPP
|
||
diff --git a/hotspot/src/share/vm/classfile/sharedClassUtil.hpp b/hotspot/src/share/vm/classfile/sharedClassUtil.hpp
|
||
index 13be2b1b5..b24e84d45 100644
|
||
--- a/hotspot/src/share/vm/classfile/sharedClassUtil.hpp
|
||
+++ b/hotspot/src/share/vm/classfile/sharedClassUtil.hpp
|
||
@@ -43,6 +43,10 @@ public:
|
||
return new FileMapInfo::FileMapHeader();
|
||
}
|
||
|
||
+ static FileMapInfo::DynamicArchiveHeader* allocate_dynamic_archive_header() {
|
||
+ return new FileMapInfo::DynamicArchiveHeader();
|
||
+ }
|
||
+
|
||
static size_t file_map_header_size() {
|
||
return sizeof(FileMapInfo::FileMapHeader);
|
||
}
|
||
diff --git a/hotspot/src/share/vm/classfile/symbolTable.cpp b/hotspot/src/share/vm/classfile/symbolTable.cpp
|
||
index 8dd4e6b21..6a2d8077f 100644
|
||
--- a/hotspot/src/share/vm/classfile/symbolTable.cpp
|
||
+++ b/hotspot/src/share/vm/classfile/symbolTable.cpp
|
||
@@ -23,6 +23,8 @@
|
||
*/
|
||
|
||
#include "precompiled.hpp"
|
||
+#include "cds/archiveBuilder.hpp"
|
||
+#include "cds/dynamicArchive.hpp"
|
||
#include "classfile/altHashing.hpp"
|
||
#include "classfile/javaClasses.hpp"
|
||
#include "classfile/symbolTable.hpp"
|
||
@@ -42,6 +44,19 @@
|
||
|
||
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
|
||
|
||
+inline bool symbol_equals_compact_hashtable_entry(Symbol* value, const char* key, int len) {
|
||
+ if (value->equals(key, len)) {
|
||
+ return true;
|
||
+ } else {
|
||
+ return false;
|
||
+ }
|
||
+}
|
||
+
|
||
+static OffsetCompactHashtable<
|
||
+ const char*, Symbol*,
|
||
+ symbol_equals_compact_hashtable_entry
|
||
+> _dynamic_shared_table;
|
||
+
|
||
// --------------------------------------------------------------------------
|
||
|
||
// the number of buckets a thread claims
|
||
@@ -95,6 +110,7 @@ void SymbolTable::symbols_do(SymbolClosure *cl) {
|
||
int SymbolTable::_symbols_removed = 0;
|
||
int SymbolTable::_symbols_counted = 0;
|
||
volatile int SymbolTable::_parallel_claimed_idx = 0;
|
||
+volatile bool _lookup_shared_first = false;
|
||
|
||
void SymbolTable::buckets_unlink(int start_idx, int end_idx, BucketUnlinkContext* context, size_t* memory_total) {
|
||
for (int i = start_idx; i < end_idx; ++i) {
|
||
@@ -228,7 +244,22 @@ Symbol* SymbolTable::lookup(int index, const char* name,
|
||
java_lang_String::hash_code((const jbyte*)s, len);
|
||
}
|
||
|
||
+#if INCLUDE_CDS
|
||
+Symbol* SymbolTable::lookup_shared(const char* name,
|
||
+ int len, unsigned int hash) {
|
||
+ Symbol* sym = NULL;
|
||
+ if (DynamicArchive::is_mapped()) {
|
||
+ if (use_alternate_hashcode()) {
|
||
+ // hash_code parameter may use alternate hashing algorithm but the shared table
|
||
+ // always uses the same original hash code.
|
||
+ hash = java_lang_String::hash_code((const jbyte*)name, len);
|
||
+ }
|
||
|
||
+ sym = _dynamic_shared_table.lookup(name, hash, len);
|
||
+ }
|
||
+ return sym;
|
||
+}
|
||
+#endif
|
||
// We take care not to be blocking while holding the
|
||
// SymbolTable_lock. Otherwise, the system might deadlock, since the
|
||
// symboltable is used during compilation (VM_thread) The lock free
|
||
@@ -251,13 +282,33 @@ unsigned int SymbolTable::hash_symbol(const char* s, int len) {
|
||
return len;
|
||
}
|
||
|
||
-Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) {
|
||
+Symbol* SymbolTable::lookup_common(const char* name, int len) {
|
||
len = check_length(name, len);
|
||
unsigned int hashValue = hash_symbol(name, len);
|
||
int index = the_table()->hash_to_index(hashValue);
|
||
+ Symbol* s;
|
||
+ if (_lookup_shared_first) {
|
||
+ s = lookup_shared(name, len, hashValue);
|
||
+ if (s == NULL) {
|
||
+ _lookup_shared_first = false;
|
||
+ s = the_table()->lookup(index, name, len, hashValue);
|
||
+ }
|
||
+ } else {
|
||
+ s = the_table()->lookup(index, name, len, hashValue);
|
||
+ if (s == NULL) {
|
||
+ s = lookup_shared(name, len, hashValue);
|
||
+ if (s!= NULL) {
|
||
+ _lookup_shared_first = true;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ return s;
|
||
+}
|
||
|
||
- Symbol* s = the_table()->lookup(index, name, len, hashValue);
|
||
-
|
||
+Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) {
|
||
+ unsigned int hashValue = hash_symbol(name, len);
|
||
+ int index = the_table()->hash_to_index(hashValue);
|
||
+ Symbol* s = lookup_common(name, len);
|
||
// Found
|
||
if (s != NULL) return s;
|
||
|
||
@@ -264,8 +315,7 @@ Symbol* SymbolTable::lookup(const Symbol* sym, int begin, int end, TRAPS) {
|
||
len = end - begin;
|
||
hashValue = hash_symbol(name, len);
|
||
index = the_table()->hash_to_index(hashValue);
|
||
- Symbol* s = the_table()->lookup(index, name, len, hashValue);
|
||
-
|
||
+ Symbol* s = lookup_common(name, len);
|
||
// Found
|
||
if (s != NULL) return s;
|
||
}
|
||
@@ -294,9 +344,7 @@ Symbol* SymbolTable::lookup(const Symbol* sym, int begin, int end, TRAPS) {
|
||
Symbol* SymbolTable::lookup_only(const char* name, int len,
|
||
unsigned int& hash) {
|
||
hash = hash_symbol(name, len);
|
||
- int index = the_table()->hash_to_index(hash);
|
||
-
|
||
- Symbol* s = the_table()->lookup(index, name, len, hash);
|
||
+ Symbol* s = lookup_common(name, len);
|
||
return s;
|
||
}
|
||
|
||
@@ -501,6 +549,42 @@ void SymbolTable::dump(outputStream* st) {
|
||
the_table()->dump_table(st, "SymbolTable");
|
||
}
|
||
|
||
+static uintx hash_shared_symbol(const char* s, int len) {
|
||
+ return java_lang_String::hash_code((const jbyte*)s, len);
|
||
+}
|
||
+
|
||
+void SymbolTable::copy_shared_symbol_table(GrowableArray<Symbol*>* symbols,
|
||
+ CompactHashtableWriter* writer) {
|
||
+ ArchiveBuilder* builder = ArchiveBuilder::current();
|
||
+ int len = symbols->length();
|
||
+ for (int i = 0; i < len; i++) {
|
||
+ Symbol* sym = ArchiveBuilder::get_relocated_symbol(symbols->at(i));
|
||
+ unsigned int fixed_hash = hash_shared_symbol((const char*)sym->bytes(), sym->utf8_length());
|
||
+ assert(fixed_hash == hash_symbol((const char*)sym->bytes(), sym->utf8_length()),
|
||
+ "must not rehash during dumping");
|
||
+ sym->set_permanent();
|
||
+ writer->add(fixed_hash, builder->buffer_to_offset_u4((address)sym));
|
||
+ }
|
||
+}
|
||
+
|
||
+size_t SymbolTable::estimate_size_for_archive() {
|
||
+ return CompactHashtableWriter::estimate_size(the_table()->number_of_entries());
|
||
+}
|
||
+
|
||
+void SymbolTable::write_to_archive(GrowableArray<Symbol*>* symbols) {
|
||
+ CompactHashtableWriter writer(symbols->length(), ArchiveBuilder::symbol_stats());
|
||
+ copy_shared_symbol_table(symbols, &writer);
|
||
+ _dynamic_shared_table.reset();
|
||
+ writer.dump(&_dynamic_shared_table, "symbol");
|
||
+}
|
||
+
|
||
+void SymbolTable::serialize_shared_table_header(SerializeClosure* soc) {
|
||
+ _dynamic_shared_table.serialize_header(soc);
|
||
+ if (soc->writing()) {
|
||
+ // Sanity. Make sure we don't use the shared table at dump time
|
||
+ _dynamic_shared_table.reset();
|
||
+ }
|
||
+}
|
||
|
||
//---------------------------------------------------------------------------
|
||
// Non-product code
|
||
diff --git a/hotspot/src/share/vm/classfile/symbolTable.hpp b/hotspot/src/share/vm/classfile/symbolTable.hpp
|
||
index 58fd22343..96eb173d1 100644
|
||
--- a/hotspot/src/share/vm/classfile/symbolTable.hpp
|
||
+++ b/hotspot/src/share/vm/classfile/symbolTable.hpp
|
||
@@ -25,6 +25,7 @@
|
||
#ifndef SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP
|
||
#define SHARE_VM_CLASSFILE_SYMBOLTABLE_HPP
|
||
|
||
+#include "classfile/compactHashtable.hpp"
|
||
#include "memory/allocation.inline.hpp"
|
||
#include "oops/symbol.hpp"
|
||
#include "utilities/hashtable.hpp"
|
||
@@ -107,6 +108,10 @@ private:
|
||
add(loader_data, cp, names_count, name, lengths, cp_indices, hashValues, THREAD);
|
||
}
|
||
|
||
+ static Symbol* lookup_shared(const char* name, int len, unsigned int hash) NOT_CDS_RETURN_(NULL);
|
||
+
|
||
+ static Symbol* lookup_common(const char* name, int len);
|
||
+
|
||
Symbol* lookup(int index, const char* name, int len, unsigned int hash);
|
||
|
||
SymbolTable()
|
||
@@ -237,6 +242,10 @@ public:
|
||
static void dump(outputStream* st);
|
||
|
||
// Sharing
|
||
+private:
|
||
+ static void copy_shared_symbol_table(GrowableArray<Symbol*>* symbols,
|
||
+ CompactHashtableWriter* ch_table);
|
||
+public:
|
||
static void copy_buckets(char** top, char*end) {
|
||
the_table()->Hashtable<Symbol*, mtSymbol>::copy_buckets(top, end);
|
||
}
|
||
@@ -246,6 +255,9 @@ public:
|
||
static void reverse(void* boundary = NULL) {
|
||
the_table()->Hashtable<Symbol*, mtSymbol>::reverse(boundary);
|
||
}
|
||
+ static size_t estimate_size_for_archive();
|
||
+ static void write_to_archive(GrowableArray<Symbol*>* symbols);
|
||
+ static void serialize_shared_table_header(SerializeClosure* soc);
|
||
|
||
// Rehash the symbol table if it gets out of balance
|
||
static void rehash_table();
|
||
diff --git a/hotspot/src/share/vm/classfile/systemDictionary.cpp b/hotspot/src/share/vm/classfile/systemDictionary.cpp
|
||
index 0d937c3ba..0ea2d9b79 100644
|
||
--- a/hotspot/src/share/vm/classfile/systemDictionary.cpp
|
||
+++ b/hotspot/src/share/vm/classfile/systemDictionary.cpp
|
||
@@ -31,6 +31,7 @@
|
||
#include "classfile/resolutionErrors.hpp"
|
||
#include "classfile/systemDictionary.hpp"
|
||
#if INCLUDE_CDS
|
||
+#include "cds/dynamicArchive.hpp"
|
||
#include "classfile/sharedClassUtil.hpp"
|
||
#include "classfile/systemDictionaryShared.hpp"
|
||
#endif
|
||
@@ -185,6 +186,11 @@ bool SystemDictionary::is_app_class_loader(Handle class_loader) {
|
||
return (class_loader->klass()->name() == vmSymbols::sun_misc_Launcher_AppClassLoader());
|
||
}
|
||
|
||
+bool SystemDictionary::is_builtin_loader(Handle class_loader) {
|
||
+ return class_loader.is_null() ||
|
||
+ class_loader->klass()->name() == vmSymbols::sun_misc_Launcher_AppClassLoader() ||
|
||
+ class_loader->klass()->name() == vmSymbols::sun_misc_Launcher_ExtClassLoader();
|
||
+}
|
||
// ----------------------------------------------------------------------------
|
||
// Resolving of classes
|
||
|
||
@@ -1131,76 +1137,92 @@ Klass* SystemDictionary::resolve_from_stream(Symbol* class_name,
|
||
check_loader_lock_contention(lockObject, THREAD);
|
||
ObjectLocker ol(lockObject, THREAD, DoObjectLock);
|
||
|
||
+ instanceKlassHandle k;
|
||
TempNewSymbol parsed_name = NULL;
|
||
|
||
- // Parse the stream. Note that we do this even though this klass might
|
||
- // already be present in the SystemDictionary, otherwise we would not
|
||
- // throw potential ClassFormatErrors.
|
||
- //
|
||
- // Note: "name" is updated.
|
||
+#if INCLUDE_CDS
|
||
+ if (DynamicArchive::is_mapped()) {
|
||
+ k = SystemDictionaryShared::lookup_from_stream(class_name,
|
||
+ class_loader,
|
||
+ protection_domain,
|
||
+ st,
|
||
+ CHECK_NULL);
|
||
+ }
|
||
+#endif
|
||
|
||
- // Callers are expected to declare a ResourceMark to determine
|
||
- // the lifetime of any updated (resource) allocated under
|
||
- // this call to parseClassFile
|
||
- ResourceMark rm(THREAD);
|
||
- ClassFileParser parser(st);
|
||
- instanceKlassHandle k = parser.parseClassFile(class_name,
|
||
- loader_data,
|
||
- protection_domain,
|
||
- parsed_name,
|
||
- verify,
|
||
- THREAD);
|
||
-
|
||
- const char* pkg = "java/";
|
||
- size_t pkglen = strlen(pkg);
|
||
- if (!HAS_PENDING_EXCEPTION &&
|
||
- !class_loader.is_null() &&
|
||
- parsed_name != NULL &&
|
||
- parsed_name->utf8_length() >= (int)pkglen) {
|
||
- ResourceMark rm(THREAD);
|
||
- bool prohibited;
|
||
- const jbyte* base = parsed_name->base();
|
||
- if ((base[0] | base[1] | base[2] | base[3] | base[4]) & 0x80) {
|
||
- prohibited = is_prohibited_package_slow(parsed_name);
|
||
- } else {
|
||
- char* name = parsed_name->as_C_string();
|
||
- prohibited = (strncmp(name, pkg, pkglen) == 0);
|
||
- }
|
||
- if (prohibited) {
|
||
- // It is illegal to define classes in the "java." package from
|
||
- // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader
|
||
- char* name = parsed_name->as_C_string();
|
||
- char* index = strrchr(name, '/');
|
||
- assert(index != NULL, "must be");
|
||
- *index = '\0'; // chop to just the package name
|
||
- while ((index = strchr(name, '/')) != NULL) {
|
||
- *index = '.'; // replace '/' with '.' in package name
|
||
+ if (k() != NULL) {
|
||
+ parsed_name = k->name();
|
||
+ } else {
|
||
+ // Parse the stream. Note that we do this even though this klass might
|
||
+ // already be present in the SystemDictionary, otherwise we would not
|
||
+ // throw potential ClassFormatErrors.
|
||
+ //
|
||
+ // Note: "name" is updated.
|
||
+
|
||
+ // Callers are expected to declare a ResourceMark to determine
|
||
+ // the lifetime of any updated (resource) allocated under
|
||
+ // this call to parseClassFile
|
||
+ ResourceMark rm(THREAD);
|
||
+ ClassFileParser parser(st);
|
||
+ k = parser.parseClassFile(class_name,
|
||
+ loader_data,
|
||
+ protection_domain,
|
||
+ parsed_name,
|
||
+ verify,
|
||
+ THREAD);
|
||
+ const char* pkg = "java/";
|
||
+ size_t pkglen = strlen(pkg);
|
||
+ if (!HAS_PENDING_EXCEPTION &&
|
||
+ !class_loader.is_null() &&
|
||
+ parsed_name != NULL &&
|
||
+ parsed_name->utf8_length() >= (int)pkglen) {
|
||
+ ResourceMark rm(THREAD);
|
||
+ bool prohibited;
|
||
+ const jbyte* base = parsed_name->base();
|
||
+ if ((base[0] | base[1] | base[2] | base[3] | base[4]) & 0x80) {
|
||
+ prohibited = is_prohibited_package_slow(parsed_name);
|
||
+ } else {
|
||
+ char* name = parsed_name->as_C_string();
|
||
+ prohibited = (strncmp(name, pkg, pkglen) == 0);
|
||
}
|
||
- const char* fmt = "Prohibited package name: %s";
|
||
- size_t len = strlen(fmt) + strlen(name);
|
||
- char* message = NEW_RESOURCE_ARRAY(char, len);
|
||
- jio_snprintf(message, len, fmt, name);
|
||
- Exceptions::_throw_msg(THREAD_AND_LOCATION,
|
||
- vmSymbols::java_lang_SecurityException(), message);
|
||
- }
|
||
- }
|
||
+ if (prohibited) {
|
||
+ // It is illegal to define classes in the "java." package from
|
||
+ // JVM_DefineClass or jni_DefineClass unless you're the bootclassloader
|
||
+ char* name = parsed_name->as_C_string();
|
||
+ char* index = strrchr(name, '/');
|
||
+ assert(index != NULL, "must be");
|
||
+ *index = '\0'; // chop to just the package name
|
||
+ while ((index = strchr(name, '/')) != NULL) {
|
||
+ *index = '.'; // replace '/' with '.' in package name
|
||
+ }
|
||
+ const char* fmt = "Prohibited package name: %s";
|
||
+ size_t len = strlen(fmt) + strlen(name);
|
||
+ char* message = NEW_RESOURCE_ARRAY(char, len);
|
||
+ jio_snprintf(message, len, fmt, name);
|
||
+ Exceptions::_throw_msg(THREAD_AND_LOCATION,
|
||
+ vmSymbols::java_lang_SecurityException(), message);
|
||
+ }
|
||
+ }
|
||
|
||
- if (!HAS_PENDING_EXCEPTION) {
|
||
- assert(parsed_name != NULL, "Sanity");
|
||
- assert(class_name == NULL || class_name == parsed_name, "name mismatch");
|
||
- // Verification prevents us from creating names with dots in them, this
|
||
- // asserts that that's the case.
|
||
- assert(is_internal_format(parsed_name),
|
||
- "external class name format used internally");
|
||
+ if (!HAS_PENDING_EXCEPTION) {
|
||
+ assert(parsed_name != NULL, "Sanity");
|
||
+ assert(class_name == NULL || class_name == parsed_name, "name mismatch");
|
||
+ // Verification prevents us from creating names with dots in them, this
|
||
+ // asserts that that's the case.
|
||
+ assert(is_internal_format(parsed_name),
|
||
+ "external class name format used internally");
|
||
|
||
#if INCLUDE_JFR
|
||
- {
|
||
- InstanceKlass* ik = k();
|
||
- ON_KLASS_CREATION(ik, parser, THREAD);
|
||
- k = instanceKlassHandle(ik);
|
||
- }
|
||
+ {
|
||
+ InstanceKlass* ik = k();
|
||
+ ON_KLASS_CREATION(ik, parser, THREAD);
|
||
+ k = instanceKlassHandle(ik);
|
||
+ }
|
||
#endif
|
||
+ }
|
||
+ }
|
||
|
||
+ if (!HAS_PENDING_EXCEPTION) {
|
||
// Add class just loaded
|
||
// If a class loader supports parallel classloading handle parallel define requests
|
||
// find_or_define_instance_class may return a different InstanceKlass
|
||
@@ -1274,14 +1296,19 @@ Klass* SystemDictionary::find_shared_class(Symbol* class_name) {
|
||
|
||
instanceKlassHandle SystemDictionary::load_shared_class(
|
||
Symbol* class_name, Handle class_loader, TRAPS) {
|
||
- if (!(class_loader.is_null() || SystemDictionary::is_app_class_loader(class_loader) ||
|
||
+ if (!(class_loader.is_null() || SystemDictionary::is_app_class_loader(class_loader) ||
|
||
SystemDictionary::is_ext_class_loader(class_loader))) {
|
||
return instanceKlassHandle();
|
||
}
|
||
|
||
- instanceKlassHandle ik (THREAD, find_shared_class(class_name)); // InstanceKlass is find with null class loader.
|
||
+ Klass* klass = SystemDictionaryShared::find_dynamic_builtin_class(class_name);
|
||
+ if (klass == NULL) {
|
||
+ klass = find_shared_class(class_name);
|
||
+ }
|
||
+
|
||
+ instanceKlassHandle ik (THREAD, klass); // InstanceKlass is find with null class loader.
|
||
if (ik.not_null()) {
|
||
- if (!UseAppCDS) {
|
||
+ if (!(UseAppCDS || DynamicArchive::is_mapped())) {
|
||
// CDS logic
|
||
if (SharedClassUtil::is_shared_boot_class(ik()) && class_loader.is_null()) {
|
||
// CDS record boot class load index.
|
||
@@ -1289,7 +1316,7 @@ instanceKlassHandle SystemDictionary::load_shared_class(
|
||
return load_shared_class(ik, class_loader, protection_domain, THREAD);
|
||
}
|
||
} else {
|
||
- // AppCDS logic. Only use null loader only to load classes that
|
||
+ // AppCDS and dynamic CDS logic. Only use null loader only to load classes that
|
||
// have been dumped by null loader. For non-null class loaders,
|
||
// either the class loader data is not initialized (but also not
|
||
// null) or the same class loader is used to load previously
|
||
@@ -1424,7 +1451,7 @@ instanceKlassHandle SystemDictionary::load_shared_class(instanceKlassHandle ik,
|
||
true /* shared class */);
|
||
|
||
// register package for this class, if necessary
|
||
- if (UseAppCDS && class_loader.not_null()) {
|
||
+ if (SystemDictionary::is_app_class_loader(class_loader) || SystemDictionary::is_ext_class_loader(class_loader)) {
|
||
|
||
ResourceMark rm(THREAD);
|
||
char* name = ik->name()->as_C_string();
|
||
diff --git a/hotspot/src/share/vm/classfile/systemDictionary.hpp b/hotspot/src/share/vm/classfile/systemDictionary.hpp
|
||
index 3b9be4430..320f71865 100644
|
||
--- a/hotspot/src/share/vm/classfile/systemDictionary.hpp
|
||
+++ b/hotspot/src/share/vm/classfile/systemDictionary.hpp
|
||
@@ -652,6 +652,7 @@ public:
|
||
TRAPS);
|
||
static bool is_ext_class_loader(Handle class_loader);
|
||
static bool is_app_class_loader(Handle class_loader);
|
||
+ static bool is_builtin_loader(Handle class_loader);
|
||
|
||
protected:
|
||
static Klass* find_shared_class(Symbol* class_name);
|
||
diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp
|
||
new file mode 100644
|
||
index 000000000..99354cd4b
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.cpp
|
||
@@ -0,0 +1,911 @@
|
||
+/*
|
||
+ * Copyright (c) 2014, 2021, Oracle and/or its affiliates. All rights reserved.
|
||
+ * Copyright (c) 2022, Huawei Technologies Co., Ltd. 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 "cds/archiveBuilder.hpp"
|
||
+#include "cds/dynamicArchive.hpp"
|
||
+#include "classfile/systemDictionaryShared.hpp"
|
||
+#include "classfile/classLoaderData.inline.hpp"
|
||
+#include "runtime/arguments.hpp"
|
||
+#include "runtime/mutexLocker.hpp"
|
||
+#include "memory/metaspaceShared.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
+#include "utilities/resourceHash.hpp"
|
||
+#include "runtime/mutexLocker.hpp"
|
||
+#include "utilities/ostream.hpp"
|
||
+
|
||
+DEBUG_ONLY(bool SystemDictionaryShared::_no_class_loading_should_happen = false;)
|
||
+bool SystemDictionaryShared::_dump_in_progress = false;
|
||
+
|
||
+class DumpTimeSharedClassInfo: public CHeapObj<mtClass> {
|
||
+ bool _excluded;
|
||
+ bool _has_checked_exclusion;
|
||
+public:
|
||
+ struct DTLoaderConstraint {
|
||
+ Symbol* _name;
|
||
+ char _loader_type1;
|
||
+ char _loader_type2;
|
||
+ DTLoaderConstraint(Symbol* name, char l1, char l2) : _name(name), _loader_type1(l1), _loader_type2(l2) {
|
||
+ _name->increment_refcount();
|
||
+ }
|
||
+ DTLoaderConstraint() : _name(NULL), _loader_type1('0'), _loader_type2('0') {}
|
||
+ bool equals(const DTLoaderConstraint& t) {
|
||
+ return t._name == _name &&
|
||
+ ((t._loader_type1 == _loader_type1 && t._loader_type2 == _loader_type2) ||
|
||
+ (t._loader_type2 == _loader_type1 && t._loader_type1 == _loader_type2));
|
||
+ }
|
||
+ };
|
||
+
|
||
+ struct DTVerifierConstraint {
|
||
+ Symbol* _name;
|
||
+ Symbol* _from_name;
|
||
+ DTVerifierConstraint() : _name(NULL), _from_name(NULL) {}
|
||
+ DTVerifierConstraint(Symbol* n, Symbol* fn) : _name(n), _from_name(fn) {
|
||
+ _name->increment_refcount();
|
||
+ _from_name->increment_refcount();
|
||
+ }
|
||
+ };
|
||
+
|
||
+ InstanceKlass* _klass;
|
||
+ InstanceKlass* _nest_host;
|
||
+ bool _failed_verification;
|
||
+ bool _is_archived_lambda_proxy;
|
||
+ int _id;
|
||
+ int _clsfile_size;
|
||
+ int _clsfile_crc32;
|
||
+ GrowableArray<DTVerifierConstraint>* _verifier_constraints;
|
||
+ GrowableArray<char>* _verifier_constraint_flags;
|
||
+ GrowableArray<DTLoaderConstraint>* _loader_constraints;
|
||
+
|
||
+ DumpTimeSharedClassInfo() {
|
||
+ _klass = NULL;
|
||
+ _nest_host = NULL;
|
||
+ _failed_verification = false;
|
||
+ _is_archived_lambda_proxy = false;
|
||
+ _has_checked_exclusion = false;
|
||
+ _id = -1;
|
||
+ _clsfile_size = -1;
|
||
+ _clsfile_crc32 = -1;
|
||
+ _excluded = false;
|
||
+ _verifier_constraints = NULL;
|
||
+ _verifier_constraint_flags = NULL;
|
||
+ _loader_constraints = NULL;
|
||
+ }
|
||
+
|
||
+ void add_verification_constraint(InstanceKlass* k, Symbol* name,
|
||
+ Symbol* from_name, bool from_field_is_protected, bool from_is_array, bool from_is_object);
|
||
+ void record_linking_constraint(Symbol* name, Handle loader1, Handle loader2);
|
||
+
|
||
+ bool is_builtin() {
|
||
+ return SystemDictionaryShared::is_builtin(_klass);
|
||
+ }
|
||
+
|
||
+ int num_verifier_constraints() {
|
||
+ if (_verifier_constraint_flags != NULL) {
|
||
+ return _verifier_constraint_flags->length();
|
||
+ } else {
|
||
+ return 0;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ int num_loader_constraints() {
|
||
+ if (_loader_constraints != NULL) {
|
||
+ return _loader_constraints->length();
|
||
+ } else {
|
||
+ return 0;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ void metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ it->push(&_klass);
|
||
+ it->push(&_nest_host);
|
||
+ if (_verifier_constraints != NULL) {
|
||
+ for (int i = 0; i < _verifier_constraints->length(); i++) {
|
||
+ DTVerifierConstraint* cons = _verifier_constraints->adr_at(i);
|
||
+ it->push(&cons->_name);
|
||
+ it->push(&cons->_from_name);
|
||
+ }
|
||
+ }
|
||
+ if (_loader_constraints != NULL) {
|
||
+ for (int i = 0; i < _loader_constraints->length(); i++) {
|
||
+ DTLoaderConstraint* lc = _loader_constraints->adr_at(i);
|
||
+ it->push(&lc->_name);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ bool is_excluded() {
|
||
+ // _klass may become NULL due to DynamicArchiveBuilder::set_to_null
|
||
+ return _excluded || _failed_verification || _klass == NULL;
|
||
+ }
|
||
+
|
||
+ // simple accessors
|
||
+ void set_excluded() { _excluded = true; }
|
||
+ bool has_checked_exclusion() const { return _has_checked_exclusion; }
|
||
+ void set_has_checked_exclusion() { _has_checked_exclusion = true; }
|
||
+ bool failed_verification() const { return _failed_verification; }
|
||
+ void set_failed_verification() { _failed_verification = true; }
|
||
+ InstanceKlass* nest_host() const { return _nest_host; }
|
||
+ void set_nest_host(InstanceKlass* nest_host) { _nest_host = nest_host; }
|
||
+};
|
||
+
|
||
+inline unsigned DumpTimeSharedClassTable_hash(InstanceKlass* const& k) {
|
||
+ // Deterministic archive is not possible because classes can be loaded
|
||
+ // in multiple threads.
|
||
+ return primitive_hash<InstanceKlass*>(k);
|
||
+}
|
||
+
|
||
+class DumpTimeSharedClassTable: public ResourceHashtable<
|
||
+ InstanceKlass*,
|
||
+ DumpTimeSharedClassInfo,
|
||
+ &DumpTimeSharedClassTable_hash,
|
||
+ primitive_equals<InstanceKlass*>,
|
||
+ 15889, // prime number
|
||
+ ResourceObj::C_HEAP>
|
||
+{
|
||
+ int _builtin_count;
|
||
+ int _unregistered_count;
|
||
+public:
|
||
+ DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k, bool dump_in_progress) {
|
||
+ bool created = false;
|
||
+ DumpTimeSharedClassInfo* p;
|
||
+ if (!dump_in_progress) {
|
||
+ p = put_if_absent(k, &created);
|
||
+ } else {
|
||
+ p = get(k);
|
||
+ }
|
||
+ if (created) {
|
||
+ assert(!SystemDictionaryShared::no_class_loading_should_happen(),
|
||
+ "no new classes can be loaded while dumping archive");
|
||
+ p->_klass = k;
|
||
+ } else {
|
||
+ if (!dump_in_progress) {
|
||
+ assert(p->_klass == k, "Sanity");
|
||
+ }
|
||
+ }
|
||
+ return p;
|
||
+ }
|
||
+
|
||
+ class CountClassByCategory : StackObj {
|
||
+ DumpTimeSharedClassTable* _table;
|
||
+ public:
|
||
+ CountClassByCategory(DumpTimeSharedClassTable* table) : _table(table) {}
|
||
+ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
|
||
+ if (!info.is_excluded()) {
|
||
+ if (info.is_builtin()) {
|
||
+ ++ _table->_builtin_count;
|
||
+ } else {
|
||
+ ++ _table->_unregistered_count;
|
||
+ }
|
||
+ }
|
||
+ return true; // keep on iterating
|
||
+ }
|
||
+ };
|
||
+
|
||
+ void update_counts() {
|
||
+ _builtin_count = 0;
|
||
+ _unregistered_count = 0;
|
||
+ CountClassByCategory counter(this);
|
||
+ iterate(&counter);
|
||
+ }
|
||
+
|
||
+ int count_of(bool is_builtin) const {
|
||
+ if (is_builtin) {
|
||
+ return _builtin_count;
|
||
+ } else {
|
||
+ return _unregistered_count;
|
||
+ }
|
||
+ }
|
||
+};
|
||
+
|
||
+class RunTimeSharedClassInfo {
|
||
+public:
|
||
+ struct CrcInfo {
|
||
+ int _clsfile_size;
|
||
+ int _clsfile_crc32;
|
||
+ };
|
||
+
|
||
+ // This is different than DumpTimeSharedClassInfo::DTVerifierConstraint. We use
|
||
+ // u4 instead of Symbol* to save space on 64-bit CPU.
|
||
+ struct RTVerifierConstraint {
|
||
+ u4 _name;
|
||
+ u4 _from_name;
|
||
+ Symbol* name() { return (Symbol*)(SharedBaseAddress + _name);}
|
||
+ Symbol* from_name() { return (Symbol*)(SharedBaseAddress + _from_name); }
|
||
+ };
|
||
+
|
||
+ struct RTLoaderConstraint {
|
||
+ u4 _name;
|
||
+ char _loader_type1;
|
||
+ char _loader_type2;
|
||
+ Symbol* constraint_name() {
|
||
+ return (Symbol*)(SharedBaseAddress + _name);
|
||
+ }
|
||
+ };
|
||
+
|
||
+ InstanceKlass* _klass;
|
||
+ int _num_verifier_constraints;
|
||
+ int _num_loader_constraints;
|
||
+
|
||
+ // optional CrcInfo _crc; (only for UNREGISTERED classes)
|
||
+ // optional InstanceKlass* _nest_host
|
||
+ // optional RTLoaderConstraint _loader_constraint_types[_num_loader_constraints]
|
||
+ // optional RTVerifierConstraint _verifier_constraints[_num_verifier_constraints]
|
||
+ // optional char _verifier_constraint_flags[_num_verifier_constraints]
|
||
+
|
||
+private:
|
||
+ static size_t header_size_size() {
|
||
+ return sizeof(RunTimeSharedClassInfo);
|
||
+ }
|
||
+ static size_t crc_size(InstanceKlass* klass) {
|
||
+ if (!SystemDictionaryShared::is_builtin(klass)) {
|
||
+ return sizeof(CrcInfo);
|
||
+ } else {
|
||
+ return 0;
|
||
+ }
|
||
+ }
|
||
+ static size_t verifier_constraints_size(int num_verifier_constraints) {
|
||
+ return sizeof(RTVerifierConstraint) * num_verifier_constraints;
|
||
+ }
|
||
+ static size_t verifier_constraint_flags_size(int num_verifier_constraints) {
|
||
+ return sizeof(char) * num_verifier_constraints;
|
||
+ }
|
||
+ static size_t loader_constraints_size(int num_loader_constraints) {
|
||
+ return sizeof(RTLoaderConstraint) * num_loader_constraints;
|
||
+ }
|
||
+ static size_t nest_host_size(InstanceKlass* klass) {
|
||
+ assert(!klass->is_anonymous(), "klass should not be hidden right now.");
|
||
+ if (klass->is_anonymous()) {
|
||
+ return sizeof(InstanceKlass*);
|
||
+ } else {
|
||
+ return 0;
|
||
+ }
|
||
+ }
|
||
+
|
||
+public:
|
||
+ static size_t byte_size(InstanceKlass* klass, int num_verifier_constraints, int num_loader_constraints) {
|
||
+ return header_size_size() +
|
||
+ crc_size(klass) +
|
||
+ nest_host_size(klass) +
|
||
+ loader_constraints_size(num_loader_constraints) +
|
||
+ verifier_constraints_size(num_verifier_constraints) +
|
||
+ verifier_constraint_flags_size(num_verifier_constraints);
|
||
+ }
|
||
+
|
||
+private:
|
||
+ size_t crc_offset() const {
|
||
+ return header_size_size();
|
||
+ }
|
||
+
|
||
+ size_t nest_host_offset() const {
|
||
+ return crc_offset() + crc_size(_klass);
|
||
+ }
|
||
+
|
||
+ size_t loader_constraints_offset() const {
|
||
+ return nest_host_offset() + nest_host_size(_klass);
|
||
+ }
|
||
+ size_t verifier_constraints_offset() const {
|
||
+ return loader_constraints_offset() + loader_constraints_size(_num_loader_constraints);
|
||
+ }
|
||
+ size_t verifier_constraint_flags_offset() const {
|
||
+ return verifier_constraints_offset() + verifier_constraints_size(_num_verifier_constraints);
|
||
+ }
|
||
+
|
||
+ void check_verifier_constraint_offset(int i) const {
|
||
+ assert(0 <= i && i < _num_verifier_constraints, "sanity");
|
||
+ }
|
||
+
|
||
+ void check_loader_constraint_offset(int i) const {
|
||
+ assert(0 <= i && i < _num_loader_constraints, "sanity");
|
||
+ }
|
||
+
|
||
+public:
|
||
+ CrcInfo* crc() const {
|
||
+ assert(crc_size(_klass) > 0, "must be");
|
||
+ return (CrcInfo*)(address(this) + crc_offset());
|
||
+ }
|
||
+ RTVerifierConstraint* verifier_constraints() {
|
||
+ assert(_num_verifier_constraints > 0, "sanity");
|
||
+ return (RTVerifierConstraint*)(address(this) + verifier_constraints_offset());
|
||
+ }
|
||
+ RTVerifierConstraint* verifier_constraint_at(int i) {
|
||
+ check_verifier_constraint_offset(i);
|
||
+ return verifier_constraints() + i;
|
||
+ }
|
||
+
|
||
+ char* verifier_constraint_flags() {
|
||
+ assert(_num_verifier_constraints > 0, "sanity");
|
||
+ return (char*)(address(this) + verifier_constraint_flags_offset());
|
||
+ }
|
||
+
|
||
+ RTLoaderConstraint* loader_constraints() {
|
||
+ assert(_num_loader_constraints > 0, "sanity");
|
||
+ return (RTLoaderConstraint*)(address(this) + loader_constraints_offset());
|
||
+ }
|
||
+
|
||
+ RTLoaderConstraint* loader_constraint_at(int i) {
|
||
+ check_loader_constraint_offset(i);
|
||
+ return loader_constraints() + i;
|
||
+ }
|
||
+
|
||
+ void init(DumpTimeSharedClassInfo& info) {
|
||
+ ArchiveBuilder* builder = ArchiveBuilder::current();
|
||
+ assert(builder->is_in_buffer_space(info._klass), "must be");
|
||
+ _klass = info._klass;
|
||
+ if (!SystemDictionaryShared::is_builtin(_klass)) {
|
||
+ CrcInfo* c = crc();
|
||
+ c->_clsfile_size = info._clsfile_size;
|
||
+ c->_clsfile_crc32 = info._clsfile_crc32;
|
||
+ }
|
||
+ _num_verifier_constraints = info.num_verifier_constraints();
|
||
+ _num_loader_constraints = info.num_loader_constraints();
|
||
+ int i;
|
||
+ if (_num_verifier_constraints > 0) {
|
||
+ RTVerifierConstraint* vf_constraints = verifier_constraints();
|
||
+ char* flags = verifier_constraint_flags();
|
||
+ for (i = 0; i < _num_verifier_constraints; i++) {
|
||
+ vf_constraints[i]._name = builder->any_to_offset_u4(info._verifier_constraints->at(i)._name);
|
||
+ vf_constraints[i]._from_name = builder->any_to_offset_u4(info._verifier_constraints->at(i)._from_name);
|
||
+ }
|
||
+ for (i = 0; i < _num_verifier_constraints; i++) {
|
||
+ flags[i] = info._verifier_constraint_flags->at(i);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (_num_loader_constraints > 0) {
|
||
+ RTLoaderConstraint* ld_constraints = loader_constraints();
|
||
+ for (i = 0; i < _num_loader_constraints; i++) {
|
||
+ ld_constraints[i]._name = builder->any_to_offset_u4(info._loader_constraints->at(i)._name);
|
||
+ ld_constraints[i]._loader_type1 = info._loader_constraints->at(i)._loader_type1;
|
||
+ ld_constraints[i]._loader_type2 = info._loader_constraints->at(i)._loader_type2;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ ArchivePtrMarker::mark_pointer(&_klass);
|
||
+ }
|
||
+
|
||
+ bool matches(int clsfile_size, int clsfile_crc32) const {
|
||
+ return crc()->_clsfile_size == clsfile_size &&
|
||
+ crc()->_clsfile_crc32 == clsfile_crc32;
|
||
+ }
|
||
+
|
||
+ char verifier_constraint_flag(int i) {
|
||
+ check_verifier_constraint_offset(i);
|
||
+ return verifier_constraint_flags()[i];
|
||
+ }
|
||
+
|
||
+private:
|
||
+ // ArchiveBuilder::make_shallow_copy() has reserved a pointer immediately
|
||
+ // before archived InstanceKlasses. We can use this slot to do a quick
|
||
+ // lookup of InstanceKlass* -> RunTimeSharedClassInfo* without
|
||
+ // building a new hashtable.
|
||
+ //
|
||
+ // info_pointer_addr(klass) --> 0x0100 RunTimeSharedClassInfo*
|
||
+ // InstanceKlass* klass --> 0x0108 <C++ vtbl>
|
||
+ // 0x0110 fields from Klass ...
|
||
+ static RunTimeSharedClassInfo** info_pointer_addr(InstanceKlass* klass) {
|
||
+ return &((RunTimeSharedClassInfo**)klass)[-1];
|
||
+ }
|
||
+
|
||
+public:
|
||
+ static RunTimeSharedClassInfo* get_for(InstanceKlass* klass) {
|
||
+ assert(klass->is_shared(), "don't call for non-shared class");
|
||
+ return *info_pointer_addr(klass);
|
||
+ }
|
||
+ static void set_for(InstanceKlass* klass, RunTimeSharedClassInfo* record) {
|
||
+ assert(ArchiveBuilder::current()->is_in_buffer_space(klass), "must be");
|
||
+ assert(ArchiveBuilder::current()->is_in_buffer_space(record), "must be");
|
||
+ *info_pointer_addr(klass) = record;
|
||
+ ArchivePtrMarker::mark_pointer(info_pointer_addr(klass));
|
||
+ }
|
||
+
|
||
+ // Used by RunTimeSharedDictionary to implement OffsetCompactHashtable::EQUALS
|
||
+ static inline bool EQUALS(
|
||
+ const RunTimeSharedClassInfo* value, Symbol* key, int len_unused) {
|
||
+ return (value->_klass->name() == key);
|
||
+ }
|
||
+};
|
||
+
|
||
+class RunTimeSharedDictionary : public OffsetCompactHashtable<
|
||
+ Symbol*,
|
||
+ const RunTimeSharedClassInfo*,
|
||
+ RunTimeSharedClassInfo::EQUALS> {};
|
||
+
|
||
+static DumpTimeSharedClassTable* _dumptime_table = NULL;
|
||
+// SystemDictionaries in the top layer dynamic archive
|
||
+static RunTimeSharedDictionary _dynamic_builtin_dictionary;
|
||
+static RunTimeSharedDictionary _dynamic_unregistered_dictionary;
|
||
+
|
||
+void SystemDictionaryShared::set_class_has_failed_verification(InstanceKlass* ik) {
|
||
+ Arguments::assert_is_dumping_archive();
|
||
+ DumpTimeSharedClassInfo* p = find_or_allocate_info_for(ik);
|
||
+ if (p != NULL) {
|
||
+ p->set_failed_verification();
|
||
+ }
|
||
+}
|
||
+
|
||
+void SystemDictionaryShared::start_dumping() {
|
||
+ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
||
+ _dump_in_progress = true;
|
||
+}
|
||
+
|
||
+void SystemDictionaryShared::init_dumptime_info(InstanceKlass* k) {
|
||
+ (void)find_or_allocate_info_for(k);
|
||
+}
|
||
+
|
||
+void SystemDictionaryShared::remove_dumptime_info(InstanceKlass* k) {
|
||
+ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
||
+ DumpTimeSharedClassInfo* p = _dumptime_table->get(k);
|
||
+ if (p == NULL) {
|
||
+ return;
|
||
+ }
|
||
+ _dumptime_table->remove(k);
|
||
+}
|
||
+
|
||
+DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for(InstanceKlass* k) {
|
||
+ MutexLockerEx ml(DumpTimeTable_lock, Mutex::_no_safepoint_check_flag);
|
||
+ return find_or_allocate_info_for_locked(k);
|
||
+}
|
||
+
|
||
+DumpTimeSharedClassInfo* SystemDictionaryShared::find_or_allocate_info_for_locked(InstanceKlass* k) {
|
||
+ assert_lock_strong(DumpTimeTable_lock);
|
||
+ if (_dumptime_table == NULL) {
|
||
+ _dumptime_table = new (ResourceObj::C_HEAP, mtClass)DumpTimeSharedClassTable();
|
||
+ }
|
||
+ return _dumptime_table->find_or_allocate_info_for(k, _dump_in_progress);
|
||
+}
|
||
+
|
||
+bool SystemDictionaryShared::empty_dumptime_table() {
|
||
+ if (_dumptime_table == NULL) {
|
||
+ return true;
|
||
+ }
|
||
+ _dumptime_table->update_counts();
|
||
+ if (_dumptime_table->count_of(true) == 0 && _dumptime_table->count_of(false) == 0) {
|
||
+ return true;
|
||
+ }
|
||
+ return false;
|
||
+}
|
||
+
|
||
+class ExcludeDumpTimeSharedClasses : StackObj {
|
||
+public:
|
||
+ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
|
||
+ SystemDictionaryShared::check_for_exclusion(k, &info);
|
||
+ return true; // keep on iterating
|
||
+ }
|
||
+};
|
||
+
|
||
+class IterateDumpTimeSharedClassTable : StackObj {
|
||
+ MetaspaceClosure *_it;
|
||
+public:
|
||
+ IterateDumpTimeSharedClassTable(MetaspaceClosure* it) : _it(it) {}
|
||
+
|
||
+ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
|
||
+ assert_lock_strong(DumpTimeTable_lock);
|
||
+ if (!info.is_excluded()) {
|
||
+ info.metaspace_pointers_do(_it);
|
||
+ }
|
||
+ return true; // keep on iterating
|
||
+ }
|
||
+};
|
||
+
|
||
+class IterateDumpTimeTableReplaceKlass : StackObj {
|
||
+public:
|
||
+ IterateDumpTimeTableReplaceKlass() { }
|
||
+
|
||
+ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
|
||
+ if (k->oop_is_instance() && !info.is_excluded()) {
|
||
+ k->constants()->symbol_replace_excluded_klass();
|
||
+ }
|
||
+ return true;
|
||
+ }
|
||
+};
|
||
+
|
||
+void SystemDictionaryShared::check_excluded_classes() {
|
||
+ assert(no_class_loading_should_happen(), "sanity");
|
||
+ assert_lock_strong(DumpTimeTable_lock);
|
||
+ ExcludeDumpTimeSharedClasses excl;
|
||
+ _dumptime_table->iterate(&excl);
|
||
+ _dumptime_table->update_counts();
|
||
+}
|
||
+
|
||
+bool SystemDictionaryShared::check_for_exclusion(InstanceKlass* k, DumpTimeSharedClassInfo* info) {
|
||
+ if (MetaspaceShared::is_in_shared_space(k)) {
|
||
+ // We have reached a super type that's already in the base archive. Treat it
|
||
+ // as "not excluded".
|
||
+ assert(DynamicDumpSharedSpaces, "must be");
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ if (info == NULL) {
|
||
+ info = _dumptime_table->get(k);
|
||
+ assert(info != NULL, "supertypes of any classes in _dumptime_table must either be shared, or must also be in _dumptime_table");
|
||
+ }
|
||
+
|
||
+ if (!info->has_checked_exclusion()) {
|
||
+ if (check_for_exclusion_impl(k)) {
|
||
+ info->set_excluded();
|
||
+ }
|
||
+ info->set_has_checked_exclusion();
|
||
+ }
|
||
+
|
||
+ return info->is_excluded();
|
||
+}
|
||
+
|
||
+// Check if a class or any of its supertypes has been redefined.
|
||
+bool SystemDictionaryShared::has_been_redefined(InstanceKlass* k) {
|
||
+ if (k->has_been_redefined()) {
|
||
+ return true;
|
||
+ }
|
||
+ if (k->java_super() != NULL && has_been_redefined(k->java_super())) {
|
||
+ return true;
|
||
+ }
|
||
+ Array<Klass*>* interfaces = k->local_interfaces();
|
||
+ int len = interfaces->length();
|
||
+ for (int i = 0; i < len; i++) {
|
||
+ if (has_been_redefined((InstanceKlass*)interfaces->at(i))) {
|
||
+ return true;
|
||
+ }
|
||
+ }
|
||
+ return false;
|
||
+}
|
||
+
|
||
+bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) {
|
||
+ if (k->is_in_error_state()) {
|
||
+ return warn_excluded(k, "In error state");
|
||
+ }
|
||
+ if (k->init_state() < InstanceKlass::loaded) {
|
||
+ return warn_excluded(k, "not loaded klass");
|
||
+ }
|
||
+ if (has_been_redefined(k)) {
|
||
+ return warn_excluded(k, "Has been redefined");
|
||
+ }
|
||
+ if (k->signers() != NULL) {
|
||
+ // We cannot include signed classes in the archive because the certificates
|
||
+ // used during dump time may be different than those used during
|
||
+ // runtime (due to expiration, etc).
|
||
+ return warn_excluded(k, "Signed JAR");
|
||
+ }
|
||
+ if (is_jfr_event_class(k)) {
|
||
+ // We cannot include JFR event classes because they need runtime-specific
|
||
+ // instrumentation in order to work with -XX:FlightRecorderOptions:retransform=false.
|
||
+ // There are only a small number of these classes, so it's not worthwhile to
|
||
+ // support them and make CDS more complicated.
|
||
+ return warn_excluded(k, "JFR event class");
|
||
+ }
|
||
+ if (k->init_state() < InstanceKlass::linked) {
|
||
+ // In CDS dumping, we will attempt to link all classes. Those that fail to link will
|
||
+ // be recorded in DumpTimeSharedClassInfo.
|
||
+ Arguments::assert_is_dumping_archive();
|
||
+
|
||
+ // TODO -- rethink how this can be handled.
|
||
+ // We should try to link ik, however, we can't do it here because
|
||
+ // 1. We are at VM exit
|
||
+ // 2. linking a class may cause other classes to be loaded, which means
|
||
+ // a custom ClassLoader.loadClass() may be called, at a point where the
|
||
+ // class loader doesn't expect it.
|
||
+ if (has_class_failed_verification(k)) {
|
||
+ return warn_excluded(k, "Failed verification");
|
||
+ } else {
|
||
+ if (k->can_be_verified_at_dumptime()) {
|
||
+ return warn_excluded(k, "Not linked");
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ if (DynamicDumpSharedSpaces && k->major_version() < 50 /*JAVA_6_VERSION*/) {
|
||
+ // In order to support old classes during dynamic dump, class rewriting needs to
|
||
+ // be reverted. This would result in more complex code and testing but not much gain.
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("Pre JDK 6 class not supported by CDS: %u.%u %s",
|
||
+ k->major_version(), k->minor_version(), k->name()->as_C_string());
|
||
+ return true;
|
||
+ }
|
||
+
|
||
+ if (!k->can_be_verified_at_dumptime() && k->is_linked()) {
|
||
+ return warn_excluded(k, "Old class has been linked");
|
||
+ }
|
||
+
|
||
+ if (k->is_anonymous() /* && !is_registered_lambda_proxy_class(k) */) {
|
||
+ return warn_excluded(k, "Hidden class");
|
||
+ }
|
||
+
|
||
+ InstanceKlass* super = k->java_super();
|
||
+ if (super != NULL && check_for_exclusion(super, NULL)) {
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("Skipping %s: super class %s is excluded", k->name()->as_C_string(), super->name()->as_C_string());
|
||
+ return true;
|
||
+ }
|
||
+
|
||
+ Array<Klass*>* interfaces = k->local_interfaces();
|
||
+ int len = interfaces->length();
|
||
+ for (int i = 0; i < len; i++) {
|
||
+ InstanceKlass* intf = (InstanceKlass*)interfaces->at(i);
|
||
+ if (check_for_exclusion(intf, NULL)) {
|
||
+ dynamic_cds_log->print_cr("Skipping %s: interface %s is excluded", k->name()->as_C_string(), intf->name()->as_C_string());
|
||
+ return true;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ return false; // false == k should NOT be excluded
|
||
+}
|
||
+
|
||
+// Returns true so the caller can do: return warn_excluded(".....");
|
||
+bool SystemDictionaryShared::warn_excluded(InstanceKlass* k, const char* reason) {
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("Skipping %s: %s", k->name()->as_C_string(), reason);
|
||
+ return true;
|
||
+}
|
||
+
|
||
+bool SystemDictionaryShared::is_jfr_event_class(InstanceKlass *k) {
|
||
+ while (k) {
|
||
+ if (k->name()->equals("jdk/jfr/Event")) {
|
||
+ return true;
|
||
+ }
|
||
+ k = k->java_super();
|
||
+ }
|
||
+ return false;
|
||
+}
|
||
+
|
||
+bool SystemDictionaryShared::has_class_failed_verification(InstanceKlass* ik) {
|
||
+ if (_dumptime_table == NULL) {
|
||
+ assert(DynamicDumpSharedSpaces, "sanity");
|
||
+ assert(ik->is_shared(), "must be a shared class in the static archive");
|
||
+ return false;
|
||
+ }
|
||
+ DumpTimeSharedClassInfo* p = _dumptime_table->get(ik);
|
||
+ return (p == NULL) ? false : p->failed_verification();
|
||
+}
|
||
+
|
||
+void SystemDictionaryShared::dumptime_classes_do(class MetaspaceClosure* it) {
|
||
+ assert_lock_strong(DumpTimeTable_lock);
|
||
+ IterateDumpTimeSharedClassTable iter(it);
|
||
+ _dumptime_table->iterate(&iter);
|
||
+}
|
||
+
|
||
+void SystemDictionaryShared::replace_klass_in_constantPool() {
|
||
+ IterateDumpTimeTableReplaceKlass iter;
|
||
+ _dumptime_table->iterate(&iter);
|
||
+}
|
||
+
|
||
+bool SystemDictionaryShared::is_excluded_class(InstanceKlass* k) {
|
||
+ assert(_no_class_loading_should_happen, "sanity");
|
||
+ assert_lock_strong(DumpTimeTable_lock);
|
||
+ Arguments::assert_is_dumping_archive();
|
||
+ DumpTimeSharedClassInfo* p = find_or_allocate_info_for_locked(k);
|
||
+ return (p == NULL) ? true : p->is_excluded();
|
||
+}
|
||
+
|
||
+class EstimateSizeForArchive : StackObj {
|
||
+ size_t _shared_class_info_size;
|
||
+ int _num_builtin_klasses;
|
||
+ int _num_unregistered_klasses;
|
||
+
|
||
+public:
|
||
+ EstimateSizeForArchive() {
|
||
+ _shared_class_info_size = 0;
|
||
+ _num_builtin_klasses = 0;
|
||
+ _num_unregistered_klasses = 0;
|
||
+ }
|
||
+
|
||
+ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
|
||
+ if (!info.is_excluded()) {
|
||
+ size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints());
|
||
+ _shared_class_info_size += align_up(byte_size, KlassAlignmentInBytes);
|
||
+ }
|
||
+ return true; // keep on iterating
|
||
+ }
|
||
+
|
||
+ size_t total() {
|
||
+ return _shared_class_info_size;
|
||
+ }
|
||
+};
|
||
+
|
||
+size_t SystemDictionaryShared::estimate_size_for_archive() {
|
||
+ EstimateSizeForArchive est;
|
||
+ _dumptime_table->iterate(&est);
|
||
+ size_t total_size = est.total() +
|
||
+ CompactHashtableWriter::estimate_size(_dumptime_table->count_of(true)) +
|
||
+ CompactHashtableWriter::estimate_size(_dumptime_table->count_of(false));
|
||
+ total_size += CompactHashtableWriter::estimate_size(0);
|
||
+ return total_size;
|
||
+}
|
||
+
|
||
+unsigned int SystemDictionaryShared::hash_for_shared_dictionary(address ptr) {
|
||
+ if (ArchiveBuilder::is_active()) {
|
||
+ uintx offset = ArchiveBuilder::current()->any_to_offset(ptr);
|
||
+ unsigned int hash = primitive_hash<uintx>(offset);
|
||
+ DEBUG_ONLY({
|
||
+ if (((const MetaspaceObj*)ptr)->is_shared()) {
|
||
+ assert(hash == SystemDictionaryShared::hash_for_shared_dictionary_quick(ptr), "must be");
|
||
+ }
|
||
+ });
|
||
+ return hash;
|
||
+ } else {
|
||
+ return SystemDictionaryShared::hash_for_shared_dictionary_quick(ptr);
|
||
+ }
|
||
+}
|
||
+
|
||
+class CopySharedClassInfoToArchive : StackObj {
|
||
+ CompactHashtableWriter* _writer;
|
||
+ bool _is_builtin;
|
||
+ ArchiveBuilder *_builder;
|
||
+public:
|
||
+ CopySharedClassInfoToArchive(CompactHashtableWriter* writer,
|
||
+ bool is_builtin)
|
||
+ : _writer(writer), _is_builtin(is_builtin), _builder(ArchiveBuilder::current()) {}
|
||
+
|
||
+ bool do_entry(InstanceKlass* k, DumpTimeSharedClassInfo& info) {
|
||
+ if (!info.is_excluded() && info.is_builtin() == _is_builtin) {
|
||
+ size_t byte_size = RunTimeSharedClassInfo::byte_size(info._klass, info.num_verifier_constraints(), info.num_loader_constraints());
|
||
+ RunTimeSharedClassInfo* record;
|
||
+ record = (RunTimeSharedClassInfo*)ArchiveBuilder::ro_region_alloc(byte_size);
|
||
+ record->init(info);
|
||
+
|
||
+ unsigned int hash;
|
||
+ Symbol* name = info._klass->name();
|
||
+ hash = SystemDictionaryShared::hash_for_shared_dictionary((address)name);
|
||
+ u4 delta = _builder->buffer_to_offset_u4((address)record);
|
||
+ if (_is_builtin && info._klass->is_anonymous()) {
|
||
+ // skip
|
||
+ } else {
|
||
+ _writer->add(hash, delta);
|
||
+ }
|
||
+ if (TraceDynamicCDS) {
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("%s dictionary: %s", (_is_builtin ? "builtin" : "unregistered"), info._klass->external_name());
|
||
+ }
|
||
+
|
||
+ // Save this for quick runtime lookup of InstanceKlass* -> RunTimeSharedClassInfo*
|
||
+ RunTimeSharedClassInfo::set_for(info._klass, record);
|
||
+ }
|
||
+ return true; // keep on iterating
|
||
+ }
|
||
+};
|
||
+
|
||
+void SystemDictionaryShared::write_dictionary(RunTimeSharedDictionary* dictionary,
|
||
+ bool is_builtin) {
|
||
+ CompactHashtableStats stats;
|
||
+ dictionary->reset();
|
||
+ CompactHashtableWriter writer(_dumptime_table->count_of(is_builtin), &stats);
|
||
+ CopySharedClassInfoToArchive copy(&writer, is_builtin);
|
||
+ assert_lock_strong(DumpTimeTable_lock);
|
||
+ _dumptime_table->iterate(©);
|
||
+ writer.dump(dictionary, is_builtin ? "builtin dictionary" : "unregistered dictionary");
|
||
+}
|
||
+
|
||
+void SystemDictionaryShared::write_to_archive() {
|
||
+ write_dictionary(&_dynamic_builtin_dictionary, true);
|
||
+ write_dictionary(&_dynamic_unregistered_dictionary, false);
|
||
+}
|
||
+
|
||
+void SystemDictionaryShared::serialize_dictionary_headers(SerializeClosure* soc) {
|
||
+ _dynamic_builtin_dictionary.serialize_header(soc);
|
||
+ _dynamic_unregistered_dictionary.serialize_header(soc);
|
||
+}
|
||
+
|
||
+void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) {
|
||
+ Arguments::assert_is_dumping_archive();
|
||
+ assert(!is_builtin(k), "must be unregistered class");
|
||
+ DumpTimeSharedClassInfo* info = find_or_allocate_info_for(k);
|
||
+ if (info != NULL) {
|
||
+ info->_clsfile_size = cfs->length();
|
||
+ info->_clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
|
||
+ }
|
||
+}
|
||
+
|
||
+// This function is called for loading only UNREGISTERED classes
|
||
+InstanceKlass* SystemDictionaryShared::lookup_from_stream(Symbol* class_name,
|
||
+ Handle class_loader,
|
||
+ Handle protection_domain,
|
||
+ const ClassFileStream* cfs,
|
||
+ TRAPS) {
|
||
+ if (!UseSharedSpaces) {
|
||
+ return NULL;
|
||
+ }
|
||
+ if (class_name == NULL) { // don't do this for hidden classes
|
||
+ return NULL;
|
||
+ }
|
||
+ if (SystemDictionary::is_builtin_loader(class_loader)) {
|
||
+ // Do nothing for the BUILTIN loaders.
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ const RunTimeSharedClassInfo* record = find_record(&_dynamic_unregistered_dictionary, class_name);
|
||
+ if (record == NULL) {
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ int clsfile_size = cfs->length();
|
||
+ int clsfile_crc32 = ClassLoader::crc32(0, (const char*)cfs->buffer(), cfs->length());
|
||
+
|
||
+ if (!record->matches(clsfile_size, clsfile_crc32)) {
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ return acquire_class_for_current_thread(record->_klass, class_loader,
|
||
+ protection_domain, cfs,
|
||
+ THREAD);
|
||
+}
|
||
+
|
||
+const RunTimeSharedClassInfo*
|
||
+SystemDictionaryShared::find_record(RunTimeSharedDictionary* dynamic_dict, Symbol* name) {
|
||
+ if (!UseSharedSpaces || !name->is_shared()) {
|
||
+ // The names of all shared classes must also be a shared Symbol.
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ unsigned int hash = SystemDictionaryShared::hash_for_shared_dictionary_quick(name);
|
||
+ const RunTimeSharedClassInfo* record = NULL;
|
||
+ // AppCDS only support builtin classloader, customer class loader is just in dynamic archive.
|
||
+ if (DynamicArchive::is_mapped()) {
|
||
+ record = dynamic_dict->lookup(name, hash, 0);
|
||
+ }
|
||
+
|
||
+ return record;
|
||
+}
|
||
+
|
||
+InstanceKlass* SystemDictionaryShared::acquire_class_for_current_thread(
|
||
+ InstanceKlass *ik,
|
||
+ Handle class_loader,
|
||
+ Handle protection_domain,
|
||
+ const ClassFileStream *cfs,
|
||
+ TRAPS) {
|
||
+ ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader());
|
||
+
|
||
+ {
|
||
+ MutexLocker mu(SharedDictionary_lock, THREAD);
|
||
+ if (ik->class_loader_data() != NULL) {
|
||
+ // ik is already loaded (by this loader or by a different loader)
|
||
+ // or ik is being loaded by a different thread (by this loader or by a different loader)
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ // No other thread has acquired this yet, so give it to *this thread*
|
||
+ ik->set_class_loader_data(loader_data);
|
||
+ }
|
||
+
|
||
+ // No longer holding SharedDictionary_lock
|
||
+ // No need to lock, as <ik> can be held only by a single thread.
|
||
+ loader_data->add_class(ik);
|
||
+
|
||
+ // Load and check super/interfaces, restore unsharable info
|
||
+ instanceKlassHandle shared_klass = SystemDictionary::load_shared_class(ik, class_loader, protection_domain, THREAD);
|
||
+ if (shared_klass() == NULL || HAS_PENDING_EXCEPTION) {
|
||
+ // TODO: clean up <ik> so it can be used again
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ return shared_klass();
|
||
+}
|
||
+
|
||
+InstanceKlass* SystemDictionaryShared::find_dynamic_builtin_class(Symbol* name) {
|
||
+ const RunTimeSharedClassInfo* record = find_record(&_dynamic_builtin_dictionary, name);
|
||
+ if (record != NULL) {
|
||
+ assert(!record->_klass->is_anonymous(), "hidden class cannot be looked up by name");
|
||
+ assert(check_klass_alignment(record->_klass), "Address not aligned");
|
||
+ return record->_klass;
|
||
+ } else {
|
||
+ return NULL;
|
||
+ }
|
||
+}
|
||
diff --git a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp
|
||
index 1bd61b02..36423bee 100644
|
||
--- a/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp
|
||
+++ b/hotspot/src/share/vm/classfile/systemDictionaryShared.hpp
|
||
@@ -22,7 +22,6 @@
|
||
*
|
||
*/
|
||
|
||
-
|
||
#ifndef SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
|
||
#define SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
|
||
|
||
@@ -30,13 +29,91 @@
|
||
#include "classfile/systemDictionary.hpp"
|
||
#include "verifier.hpp"
|
||
|
||
+/*===============================================================================
|
||
+
|
||
+ Handling of the classes in the AppCDS archive
|
||
+
|
||
+ To ensure safety and to simplify the implementation, archived classes are
|
||
+ "segregated" into 2 types. The following rules describe how they
|
||
+ are stored and looked up.
|
||
+
|
||
+[1] Category of archived classes
|
||
+
|
||
+ There are 2 disjoint groups of classes stored in the AppCDS archive:
|
||
+
|
||
+ BUILTIN: These classes may be defined ONLY by the BOOT/PLATFORM/APP
|
||
+ loaders.
|
||
+
|
||
+ UNREGISTERED: These classes may be defined ONLY by a ClassLoader
|
||
+ instance that's not listed above (using fingerprint matching)
|
||
+
|
||
+[2] How classes from different categories are specified in the classlist:
|
||
+
|
||
+ Starting from JDK9, each class in the classlist may be specified with
|
||
+ these keywords: "id", "super", "interfaces", "loader" and "source".
|
||
+
|
||
+
|
||
+ BUILTIN Only the "id" keyword may be (optionally) specified. All other
|
||
+ keywords are forbidden.
|
||
+
|
||
+ The named class is looked up from the jimage and from
|
||
+ Xbootclasspath/a and CLASSPATH.
|
||
+
|
||
+ UNREGISTERED: The "id", "super", and "source" keywords must all be
|
||
+ specified.
|
||
+
|
||
+ The "interfaces" keyword must be specified if the class implements
|
||
+ one or more local interfaces. The "interfaces" keyword must not be
|
||
+ specified if the class does not implement local interfaces.
|
||
+
|
||
+ The named class is looked up from the location specified in the
|
||
+ "source" keyword.
|
||
+
|
||
+ Example classlist:
|
||
+
|
||
+ # BUILTIN
|
||
+ java/lang/Object id: 0
|
||
+ java/lang/Cloneable id: 1
|
||
+ java/lang/String
|
||
+
|
||
+ # UNREGISTERED
|
||
+ Bar id: 3 super: 0 interfaces: 1 source: /foo.jar
|
||
+
|
||
+
|
||
+[3] Identifying the category of archived classes
|
||
+
|
||
+ BUILTIN: (C->shared_classpath_index() >= 0)
|
||
+ UNREGISTERED: (C->shared_classpath_index() == UNREGISTERED_INDEX (-9999))
|
||
+
|
||
+[4] Lookup of archived classes at run time:
|
||
+
|
||
+ (a) BUILTIN loaders:
|
||
+
|
||
+ search _builtin_dictionary
|
||
+
|
||
+ (b) UNREGISTERED loaders:
|
||
+
|
||
+ search _unregistered_dictionary for an entry that matches the
|
||
+ (name, clsfile_len, clsfile_crc32).
|
||
+
|
||
+===============================================================================*/
|
||
+#define UNREGISTERED_INDEX -9999
|
||
+
|
||
+class DumpTimeSharedClassInfo;
|
||
+class RunTimeSharedClassInfo;
|
||
+class RunTimeSharedDictionary;
|
||
+
|
||
class SystemDictionaryShared: public SystemDictionary {
|
||
+private:
|
||
+ static bool _dump_in_progress;
|
||
+ DEBUG_ONLY(static bool _no_class_loading_should_happen;)
|
||
+
|
||
public:
|
||
static void initialize(TRAPS) {}
|
||
static instanceKlassHandle find_or_load_shared_class(Symbol* class_name,
|
||
Handle class_loader,
|
||
TRAPS) {
|
||
- if (UseAppCDS) {
|
||
+ if (UseSharedSpaces) {
|
||
instanceKlassHandle ik = load_shared_class(class_name, class_loader, CHECK_NULL);
|
||
if (!ik.is_null()) {
|
||
instanceKlassHandle nh = instanceKlassHandle(); // null Handle
|
||
@@ -48,7 +125,7 @@ public:
|
||
}
|
||
static void roots_oops_do(OopClosure* blk) {}
|
||
static void oops_do(OopClosure* f) {}
|
||
-
|
||
+
|
||
static bool is_sharing_possible(ClassLoaderData* loader_data) {
|
||
oop class_loader = loader_data->class_loader();
|
||
return (class_loader == NULL ||
|
||
@@ -60,8 +137,43 @@ public:
|
||
static size_t dictionary_entry_size() {
|
||
return sizeof(DictionaryEntry);
|
||
}
|
||
+
|
||
static void init_shared_dictionary_entry(Klass* k, DictionaryEntry* entry) {}
|
||
|
||
+ static void init_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
|
||
+ static void remove_dumptime_info(InstanceKlass* k) NOT_CDS_RETURN;
|
||
+
|
||
+ static void start_dumping();
|
||
+
|
||
+ static DumpTimeSharedClassInfo* find_or_allocate_info_for(InstanceKlass* k);
|
||
+
|
||
+ static DumpTimeSharedClassInfo* find_or_allocate_info_for_locked(InstanceKlass* k);
|
||
+
|
||
+ static bool empty_dumptime_table();
|
||
+
|
||
+ static void check_excluded_classes();
|
||
+
|
||
+ static bool check_for_exclusion(InstanceKlass* k, DumpTimeSharedClassInfo* info);
|
||
+
|
||
+ static bool has_been_redefined(InstanceKlass* k);
|
||
+
|
||
+ static bool check_for_exclusion_impl(InstanceKlass* k);
|
||
+
|
||
+ static bool warn_excluded(InstanceKlass* k, const char* reason);
|
||
+
|
||
+ static bool is_jfr_event_class(InstanceKlass *k);
|
||
+
|
||
+ static bool has_class_failed_verification(InstanceKlass* ik);
|
||
+
|
||
+ static bool is_builtin(InstanceKlass* k) {
|
||
+ return (k->shared_classpath_index() != UNREGISTERED_INDEX);
|
||
+ }
|
||
+
|
||
+ static void dumptime_classes_do(class MetaspaceClosure* it);
|
||
+
|
||
+ static void replace_klass_in_constantPool();
|
||
+
|
||
+ static bool is_excluded_class(InstanceKlass* k);
|
||
// The (non-application) CDS implementation supports only classes in the boot
|
||
// class loader, which ensures that the verification dependencies are the same
|
||
// during archive creation time and runtime. Thus we can do the dependency checks
|
||
@@ -69,6 +181,7 @@ public:
|
||
static void add_verification_dependency(Klass* k, Symbol* accessor_clsname,
|
||
Symbol* target_clsname) {}
|
||
static void finalize_verification_dependencies() {}
|
||
+ static void set_class_has_failed_verification(InstanceKlass* ik);
|
||
static bool check_verification_dependencies(Klass* k, Handle class_loader,
|
||
Handle protection_domain,
|
||
char** message_buffer, TRAPS) {
|
||
@@ -81,6 +194,49 @@ public:
|
||
}
|
||
return true;
|
||
}
|
||
+ static size_t estimate_size_for_archive();
|
||
+ static void write_to_archive();
|
||
+ static void write_dictionary(RunTimeSharedDictionary* dictionary, bool is_builtin);
|
||
+ static void serialize_dictionary_headers(class SerializeClosure* soc);
|
||
+ static unsigned int hash_for_shared_dictionary(address ptr);
|
||
+ static void set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs);
|
||
+ static InstanceKlass* lookup_from_stream(Symbol* class_name,
|
||
+ Handle class_loader,
|
||
+ Handle protection_domain,
|
||
+ const ClassFileStream* cfs,
|
||
+ TRAPS);
|
||
+
|
||
+ DEBUG_ONLY(static bool no_class_loading_should_happen() {return _no_class_loading_should_happen;})
|
||
+
|
||
+#ifdef ASSERT
|
||
+ class NoClassLoadingMark: public StackObj {
|
||
+ public:
|
||
+ NoClassLoadingMark() {
|
||
+ assert(!_no_class_loading_should_happen, "must not be nested");
|
||
+ _no_class_loading_should_happen = true;
|
||
+ }
|
||
+ ~NoClassLoadingMark() {
|
||
+ _no_class_loading_should_happen = false;
|
||
+ }
|
||
+ };
|
||
+#endif
|
||
+
|
||
+ template <typename T>
|
||
+ static unsigned int hash_for_shared_dictionary_quick(T* ptr) {
|
||
+ assert(((MetaspaceObj*)ptr)->is_shared(), "must be");
|
||
+ assert(ptr > (T*)SharedBaseAddress, "must be");
|
||
+ uintx offset = uintx(ptr) - uintx(SharedBaseAddress);
|
||
+ return primitive_hash<uintx>(offset);
|
||
+ }
|
||
+
|
||
+ static const RunTimeSharedClassInfo* find_record(RunTimeSharedDictionary* dynamic_dict, Symbol* name);
|
||
+ static InstanceKlass* acquire_class_for_current_thread(InstanceKlass *ik,
|
||
+ Handle class_loader,
|
||
+ Handle protection_domain,
|
||
+ const ClassFileStream *cfs,
|
||
+ TRAPS);
|
||
+
|
||
+ static InstanceKlass* find_dynamic_builtin_class(Symbol* name);
|
||
};
|
||
|
||
#endif // SHARE_VM_CLASSFILE_SYSTEMDICTIONARYSHARED_HPP
|
||
diff --git a/hotspot/src/share/vm/memory/allocation.hpp b/hotspot/src/share/vm/memory/allocation.hpp
|
||
index aa8f02d09..4d324b442 100644
|
||
--- a/hotspot/src/share/vm/memory/allocation.hpp
|
||
+++ b/hotspot/src/share/vm/memory/allocation.hpp
|
||
@@ -302,6 +302,11 @@ class MetaspaceObj {
|
||
Type type, Thread* thread) throw();
|
||
// can't use TRAPS from this header file.
|
||
void operator delete(void* p) { ShouldNotCallThis(); }
|
||
+
|
||
+ // Declare a *static* method with the same signature in any subclass of MetaspaceObj
|
||
+ // that should be read-only by default. See symbol.hpp for an example. This function
|
||
+ // is used by the templates in metaspaceClosure.hpp
|
||
+ static bool is_read_only_by_default() { return false; }
|
||
};
|
||
|
||
// Base class for classes that constitute name spaces.
|
||
@@ -728,6 +733,12 @@ class ArrayAllocator VALUE_OBJ_CLASS_SPEC {
|
||
bool _use_malloc;
|
||
size_t _size;
|
||
bool _free_in_destructor;
|
||
+
|
||
+ static bool should_use_malloc(size_t size) {
|
||
+ return size < ArrayAllocatorMallocLimit;
|
||
+ }
|
||
+
|
||
+ static char* allocate_inner(size_t& size, bool& use_malloc);
|
||
public:
|
||
ArrayAllocator(bool free_in_destructor = true) :
|
||
_addr(NULL), _use_malloc(false), _size(0), _free_in_destructor(free_in_destructor) { }
|
||
@@ -739,6 +750,7 @@ class ArrayAllocator VALUE_OBJ_CLASS_SPEC {
|
||
}
|
||
|
||
E* allocate(size_t length);
|
||
+ E* reallocate(size_t new_length);
|
||
void free();
|
||
};
|
||
|
||
diff --git a/hotspot/src/share/vm/memory/allocation.inline.hpp b/hotspot/src/share/vm/memory/allocation.inline.hpp
|
||
index 9f2e1655a..2e794a8b6 100644
|
||
--- a/hotspot/src/share/vm/memory/allocation.inline.hpp
|
||
+++ b/hotspot/src/share/vm/memory/allocation.inline.hpp
|
||
@@ -151,35 +151,58 @@ template <MEMFLAGS F> void CHeapObj<F>::operator delete [](void* p){
|
||
}
|
||
|
||
template <class E, MEMFLAGS F>
|
||
-E* ArrayAllocator<E, F>::allocate(size_t length) {
|
||
- assert(_addr == NULL, "Already in use");
|
||
+char* ArrayAllocator<E, F>::allocate_inner(size_t &size, bool &use_malloc) {
|
||
+ char* addr = NULL;
|
||
|
||
- _size = sizeof(E) * length;
|
||
- _use_malloc = _size < ArrayAllocatorMallocLimit;
|
||
-
|
||
- if (_use_malloc) {
|
||
- _addr = AllocateHeap(_size, F);
|
||
- if (_addr == NULL && _size >= (size_t)os::vm_allocation_granularity()) {
|
||
+ if (use_malloc) {
|
||
+ addr = AllocateHeap(size, F);
|
||
+ if (addr == NULL && size >= (size_t)os::vm_allocation_granularity()) {
|
||
// malloc failed let's try with mmap instead
|
||
- _use_malloc = false;
|
||
+ use_malloc = false;
|
||
} else {
|
||
- return (E*)_addr;
|
||
+ return addr;
|
||
}
|
||
}
|
||
|
||
int alignment = os::vm_allocation_granularity();
|
||
- _size = align_size_up(_size, alignment);
|
||
+ size = align_size_up(size, alignment);
|
||
|
||
- _addr = os::reserve_memory(_size, NULL, alignment, F);
|
||
- if (_addr == NULL) {
|
||
- vm_exit_out_of_memory(_size, OOM_MMAP_ERROR, "Allocator (reserve)");
|
||
+ addr = os::reserve_memory(size, NULL, alignment, F);
|
||
+ if (addr == NULL) {
|
||
+ vm_exit_out_of_memory(size, OOM_MMAP_ERROR, "Allocator (reserve)");
|
||
}
|
||
|
||
- os::commit_memory_or_exit(_addr, _size, !ExecMem, "Allocator (commit)");
|
||
+ os::commit_memory_or_exit(addr, size, !ExecMem, "Allocator (commit)");
|
||
+ return addr;
|
||
+}
|
||
+
|
||
+template <class E, MEMFLAGS F>
|
||
+E* ArrayAllocator<E, F>::allocate(size_t length) {
|
||
+ assert(_addr == NULL, "Already in use");
|
||
|
||
+ _size = sizeof(E) * length;
|
||
+
|
||
+ _use_malloc = should_use_malloc(_size);
|
||
+ _addr = allocate_inner(_size, _use_malloc);
|
||
return (E*)_addr;
|
||
}
|
||
|
||
+template <class E, MEMFLAGS F>
|
||
+E* ArrayAllocator<E, F>::reallocate(size_t new_length) {
|
||
+ size_t new_size = sizeof(E) * new_length;
|
||
+ bool use_malloc = should_use_malloc(new_size);
|
||
+ char* new_addr = allocate_inner(new_size, use_malloc);
|
||
+
|
||
+ memcpy(new_addr, _addr, MIN2(new_size, _size));
|
||
+
|
||
+ free();
|
||
+ _size = new_size;
|
||
+ _use_malloc = use_malloc;
|
||
+ _addr = new_addr;
|
||
+ return (E*)new_addr;
|
||
+}
|
||
+
|
||
+
|
||
template<class E, MEMFLAGS F>
|
||
void ArrayAllocator<E, F>::free() {
|
||
if (_addr != NULL) {
|
||
diff --git a/hotspot/src/share/vm/memory/filemap.cpp b/hotspot/src/share/vm/memory/filemap.cpp
|
||
index 99b1f58d0..3f4106476 100644
|
||
--- a/hotspot/src/share/vm/memory/filemap.cpp
|
||
+++ b/hotspot/src/share/vm/memory/filemap.cpp
|
||
@@ -24,6 +24,8 @@
|
||
|
||
#include "jvm.h"
|
||
#include "precompiled.hpp"
|
||
+#include "cds/archiveBuilder.hpp"
|
||
+#include "cds/dynamicArchive.hpp"
|
||
#include "classfile/classLoader.hpp"
|
||
#include "classfile/sharedClassUtil.hpp"
|
||
#include "classfile/symbolTable.hpp"
|
||
@@ -140,19 +142,33 @@ template <int N> static void get_header_version(char (&header_version) [N]) {
|
||
}
|
||
}
|
||
|
||
-FileMapInfo::FileMapInfo() {
|
||
- assert(_current_info == NULL, "must be singleton"); // not thread safe
|
||
- _current_info = this;
|
||
+FileMapInfo::FileMapInfo(bool is_static) {
|
||
memset(this, 0, sizeof(FileMapInfo));
|
||
+ _is_static = is_static;
|
||
+
|
||
+ if (is_static) {
|
||
+ assert(_current_info == NULL, "must be singleton"); // not thread safe
|
||
+ _current_info = this;
|
||
+ _header = SharedClassUtil::allocate_file_map_header();
|
||
+ } else {
|
||
+ assert(_dynamic_archive_info == NULL, "must be singleton"); // not thread safe
|
||
+ _dynamic_archive_info = this;
|
||
+ _header = SharedClassUtil::allocate_dynamic_archive_header();
|
||
+ }
|
||
+
|
||
+ _header->_version = _invalid_version;
|
||
_file_offset = 0;
|
||
_file_open = false;
|
||
- _header = SharedClassUtil::allocate_file_map_header();
|
||
- _header->_version = _invalid_version;
|
||
}
|
||
|
||
FileMapInfo::~FileMapInfo() {
|
||
- assert(_current_info == this, "must be singleton"); // not thread safe
|
||
- _current_info = NULL;
|
||
+ if (_is_static) {
|
||
+ assert(_current_info == this, "must be singleton"); // not thread safe
|
||
+ _current_info = NULL;
|
||
+ } else {
|
||
+ assert(_dynamic_archive_info == this, "must be singleton"); // not thread safe
|
||
+ _dynamic_archive_info = NULL;
|
||
+ }
|
||
}
|
||
|
||
void FileMapInfo::populate_header(size_t alignment) {
|
||
@@ -163,14 +179,66 @@ size_t FileMapInfo::FileMapHeader::data_size() {
|
||
return SharedClassUtil::file_map_header_size() - sizeof(FileMapInfo::FileMapHeaderBase);
|
||
}
|
||
|
||
+size_t FileMapInfo::DynamicArchiveHeader::data_size() {
|
||
+ return sizeof(FileMapInfo::DynamicArchiveHeader) - sizeof(FileMapInfo::FileMapHeaderBase);
|
||
+}
|
||
+
|
||
+bool FileMapInfo::DynamicArchiveHeader::validate() {
|
||
+ if (_magic != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
||
+ FileMapInfo::fail_continue("The shared archive file has a bad magic number.");
|
||
+ return false;
|
||
+ }
|
||
+ if (VerifySharedSpaces && compute_crc() != _crc) {
|
||
+ fail_continue("Header checksum verification failed.");
|
||
+ return false;
|
||
+ }
|
||
+ if (_version != current_version()) {
|
||
+ FileMapInfo::fail_continue("The shared archive file is the wrong version.");
|
||
+ return false;
|
||
+ }
|
||
+ char header_version[JVM_IDENT_MAX];
|
||
+ get_header_version(header_version);
|
||
+ if (strncmp(_jvm_ident, header_version, JVM_IDENT_MAX-1) != 0) {
|
||
+ if (TraceClassPaths) {
|
||
+ tty->print_cr("Expected: %s", header_version);
|
||
+ tty->print_cr("Actual: %s", _jvm_ident);
|
||
+ }
|
||
+ FileMapInfo::fail_continue("The shared archive file was created by a different"
|
||
+ " version or build of HotSpot");
|
||
+ return false;
|
||
+ }
|
||
+ if (_obj_alignment != ObjectAlignmentInBytes) {
|
||
+ FileMapInfo::fail_continue("The shared archive file's ObjectAlignmentInBytes of %d"
|
||
+ " does not equal the current ObjectAlignmentInBytes of %d.",
|
||
+ _obj_alignment, ObjectAlignmentInBytes);
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // TODO: much more validate check
|
||
+
|
||
+ return true;
|
||
+}
|
||
+
|
||
void FileMapInfo::FileMapHeader::populate(FileMapInfo* mapinfo, size_t alignment) {
|
||
- _magic = 0xf00baba2;
|
||
- _version = _current_version;
|
||
+ if (DynamicDumpSharedSpaces) {
|
||
+ _magic = CDS_DYNAMIC_ARCHIVE_MAGIC;
|
||
+ } else {
|
||
+ _magic = CDS_ARCHIVE_MAGIC;
|
||
+ }
|
||
+ _version = current_version();
|
||
_alignment = alignment;
|
||
_obj_alignment = ObjectAlignmentInBytes;
|
||
- _classpath_entry_table_size = mapinfo->_classpath_entry_table_size;
|
||
- _classpath_entry_table = mapinfo->_classpath_entry_table;
|
||
- _classpath_entry_size = mapinfo->_classpath_entry_size;
|
||
+ /* TODO
|
||
+ _compressed_oops = UseCompressedOops;
|
||
+ _compressed_class_ptrs = UseCompressedClassPointers;
|
||
+ _max_heap_size = MaxHeapSize;
|
||
+ _narrow_klass_shift = CompressedKlassPointers::shift();
|
||
+ */
|
||
+ if (!DynamicDumpSharedSpaces) {
|
||
+ _classpath_entry_table_size = mapinfo->_classpath_entry_table_size;
|
||
+ _classpath_entry_table = mapinfo->_classpath_entry_table;
|
||
+ _classpath_entry_size = mapinfo->_classpath_entry_size;
|
||
+ }
|
||
|
||
// The following fields are for sanity checks for whether this archive
|
||
// will function correctly with this JVM and the bootclasspath it's
|
||
@@ -303,62 +371,174 @@ bool FileMapInfo::validate_classpath_entry_table() {
|
||
return true;
|
||
}
|
||
|
||
+bool FileMapInfo::get_base_archive_name_from_header(const char* archive_name,
|
||
+ int* size, char** base_archive_name) {
|
||
+ int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0);
|
||
+ if (fd < 0) {
|
||
+ *size = 0;
|
||
+ return false;
|
||
+ }
|
||
|
||
-// Read the FileMapInfo information from the file.
|
||
-
|
||
-bool FileMapInfo::init_from_file(int fd) {
|
||
- size_t sz = _header->data_size();
|
||
- char* addr = _header->data();
|
||
+ // read the header as a dynamic archive header
|
||
+ DynamicArchiveHeader* dynamic_header = SharedClassUtil::allocate_dynamic_archive_header();
|
||
+ size_t sz = dynamic_header->data_size();
|
||
+ char* addr = dynamic_header->data();
|
||
size_t n = os::read(fd, addr, (unsigned int)sz);
|
||
if (n != sz) {
|
||
fail_continue("Unable to read the file header.");
|
||
+ delete dynamic_header;
|
||
+ os::close(fd);
|
||
return false;
|
||
}
|
||
- if (_header->_version != current_version()) {
|
||
- fail_continue("The shared archive file has the wrong version.");
|
||
+ if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
||
+ // Not a dynamic header, no need to proceed further.
|
||
+ *size = 0;
|
||
+ delete dynamic_header;
|
||
+ os::close(fd);
|
||
return false;
|
||
}
|
||
|
||
- size_t info_size = _header->_paths_misc_info_size;
|
||
- _paths_misc_info = NEW_C_HEAP_ARRAY_RETURN_NULL(char, info_size, mtClass);
|
||
- if (_paths_misc_info == NULL) {
|
||
- fail_continue("Unable to read the file header.");
|
||
+ // read the base archive name
|
||
+ size_t name_size = dynamic_header->base_archive_name_size();
|
||
+ if (name_size == 0) {
|
||
+ delete dynamic_header;
|
||
+ os::close(fd);
|
||
return false;
|
||
}
|
||
- n = os::read(fd, _paths_misc_info, (unsigned int)info_size);
|
||
- if (n != info_size) {
|
||
- fail_continue("Unable to read the shared path info header.");
|
||
- FREE_C_HEAP_ARRAY(char, _paths_misc_info, mtClass);
|
||
- _paths_misc_info = NULL;
|
||
+ *base_archive_name = NEW_C_HEAP_ARRAY(char, name_size, mtInternal);
|
||
+ n = os::read(fd, *base_archive_name, (unsigned int)name_size);
|
||
+ if (n != name_size) {
|
||
+ fail_continue("Unable to read the base archive name from the header.");
|
||
+ FREE_C_HEAP_ARRAY(char, *base_archive_name, mtInternal);
|
||
+ *base_archive_name = NULL;
|
||
+ delete dynamic_header;
|
||
+ os::close(fd);
|
||
return false;
|
||
}
|
||
|
||
- size_t len = lseek(fd, 0, SEEK_END);
|
||
- struct FileMapInfo::FileMapHeader::space_info* si =
|
||
- &_header->_space[MetaspaceShared::mc];
|
||
- if (si->_file_offset >= len || len - si->_file_offset < si->_used) {
|
||
- fail_continue("The shared archive file has been truncated.");
|
||
+ delete dynamic_header;
|
||
+ os::close(fd);
|
||
+ return true;
|
||
+}
|
||
+
|
||
+bool FileMapInfo::check_archive(const char* archive_name, bool is_static) {
|
||
+ int fd = os::open(archive_name, O_RDONLY | O_BINARY, 0);
|
||
+ if (fd < 0) {
|
||
+ // do not vm_exit_during_initialization here because Arguments::init_shared_archive_paths()
|
||
+ // requires a shared archive name. The open_for_read() function will log a message regarding
|
||
+ // failure in opening a shared archive.
|
||
return false;
|
||
}
|
||
|
||
- _file_offset += (long)n;
|
||
+ FileMapHeader* header = NULL;
|
||
+ if (is_static) {
|
||
+ header = SharedClassUtil::allocate_file_map_header();
|
||
+ } else {
|
||
+ header = SharedClassUtil::allocate_dynamic_archive_header();
|
||
+ }
|
||
+
|
||
+ size_t sz = header->data_size();
|
||
+ size_t n = os::read(fd, header->data(), (unsigned int)sz);
|
||
+ if (n != sz) {
|
||
+ delete header;
|
||
+ os::close(fd);
|
||
+ vm_exit_during_initialization("Unable to read header from shared archive", archive_name);
|
||
+ return false;
|
||
+ }
|
||
+ if (is_static) {
|
||
+ FileMapHeader* static_header = (FileMapHeader*)header;
|
||
+ if (static_header->magic() != CDS_ARCHIVE_MAGIC) {
|
||
+ delete header;
|
||
+ os::close(fd);
|
||
+ vm_exit_during_initialization("Not a base shared archive", archive_name);
|
||
+ return false;
|
||
+ }
|
||
+ } else {
|
||
+ DynamicArchiveHeader* dynamic_header = (DynamicArchiveHeader*)header;
|
||
+ if (dynamic_header->magic() != CDS_DYNAMIC_ARCHIVE_MAGIC) {
|
||
+ delete header;
|
||
+ os::close(fd);
|
||
+ vm_exit_during_initialization("Not a top shared archive", archive_name);
|
||
+ return false;
|
||
+ }
|
||
+ }
|
||
+ delete header;
|
||
+ os::close(fd);
|
||
+ return true;
|
||
+}
|
||
+
|
||
+// Read the FileMapInfo information from the file.
|
||
+
|
||
+bool FileMapInfo::init_from_file(int fd) {
|
||
+ size_t sz = header()->data_size();
|
||
+ char* addr = header()->data();
|
||
+ size_t n = os::read(fd, addr, (unsigned int)sz);
|
||
+ if (n != sz) {
|
||
+ fail_continue("Unable to read the file header.");
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ _file_offset += n;
|
||
+
|
||
+ if (is_static()) {
|
||
+ size_t info_size = _header->_paths_misc_info_size;
|
||
+ _paths_misc_info = NEW_C_HEAP_ARRAY_RETURN_NULL(char, info_size, mtClass);
|
||
+ if (_paths_misc_info == NULL) {
|
||
+ fail_continue("Unable to read the file header.");
|
||
+ return false;
|
||
+ }
|
||
+ n = os::read(fd, _paths_misc_info, (unsigned int)info_size);
|
||
+ if (n != info_size) {
|
||
+ fail_continue("Unable to read the shared path info header.");
|
||
+ FREE_C_HEAP_ARRAY(char, _paths_misc_info, mtClass);
|
||
+ _paths_misc_info = NULL;
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // just checking the last region is sufficient since the archive is written
|
||
+ // in sequential order
|
||
+ size_t len = lseek(fd, 0, SEEK_END);
|
||
+ struct FileMapInfo::FileMapHeader::space_info* si =
|
||
+ &_header->_space[MetaspaceShared::mc];
|
||
+ if (si->_file_offset >= len || len - si->_file_offset < si->_used) {
|
||
+ fail_continue("The shared archive file has been truncated.");
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ _file_offset += n;
|
||
+ } else {
|
||
+ _file_offset += dynamic_header()->base_archive_name_size(); // accounts for the size of _base_archive_name
|
||
+ }
|
||
+
|
||
return true;
|
||
}
|
||
|
||
|
||
// Read the FileMapInfo information from the file.
|
||
bool FileMapInfo::open_for_read() {
|
||
- _full_path = make_log_name(Arguments::GetSharedArchivePath(), NULL);
|
||
- int fd = open(_full_path, O_RDONLY | O_BINARY, 0);
|
||
+ if (_file_open) {
|
||
+ return true;
|
||
+ }
|
||
+ if (is_static()) {
|
||
+ _full_path = Arguments::GetSharedArchivePath();
|
||
+ } else {
|
||
+ _full_path = Arguments::GetSharedDynamicArchivePath();
|
||
+ }
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("trying to map %s", _full_path);
|
||
+ }
|
||
+ 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. archive file path:%s", _full_path);
|
||
+ fail_continue("Specified shared archive not found (%s).", _full_path);
|
||
} else {
|
||
- fail_continue("Failed to open shared archive file (%s).",
|
||
- strerror(errno));
|
||
+ fail_continue("Failed to open shared archive file (%s).", strerror(errno));
|
||
}
|
||
return false;
|
||
+ } else {
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Opened archive %s.", _full_path);
|
||
+ }
|
||
}
|
||
|
||
_fd = fd;
|
||
@@ -368,7 +548,7 @@ bool FileMapInfo::open_for_read() {
|
||
|
||
// Write the FileMapInfo information to the file.
|
||
void FileMapInfo::open_for_write() {
|
||
- if (UseAppCDS && AppCDSLockFile != NULL) {
|
||
+ if ((DynamicDumpSharedSpaces || UseAppCDS) && AppCDSLockFile != NULL) {
|
||
char* pos = strrchr(const_cast<char*>(AppCDSLockFile), '/');
|
||
#ifdef __linux__
|
||
if (pos != NULL && pos != AppCDSLockFile) { // No directory path specified
|
||
@@ -391,14 +571,18 @@ void FileMapInfo::open_for_write() {
|
||
int lock_fd = open(_appcds_file_lock_path, O_CREAT | O_WRONLY | O_EXCL, S_IRUSR | S_IWUSR);
|
||
if (lock_fd < 0) {
|
||
tty->print_cr("Failed to create jsa file !\n Please check: \n 1. The directory exists.\n "
|
||
- "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n");
|
||
+ "2. You have the permission.\n 3. Make sure no other process using the same lock file.\n");
|
||
fail_stop("Failed to create appcds lock file, the lock path is: %s.", _appcds_file_lock_path);
|
||
}
|
||
tty->print_cr("You are using file lock %s in concurrent mode", AppCDSLockFile);
|
||
}
|
||
#endif
|
||
}
|
||
- _full_path = make_log_name(Arguments::GetSharedArchivePath(), NULL);
|
||
+ if (is_static()) {
|
||
+ _full_path = make_log_name(Arguments::GetSharedArchivePath(), NULL);
|
||
+ } else {
|
||
+ _full_path = make_log_name(Arguments::GetSharedDynamicArchivePath(), NULL);
|
||
+ }
|
||
if (PrintSharedSpaces) {
|
||
tty->print_cr("Dumping shared data to file: ");
|
||
tty->print_cr(" %s", _full_path);
|
||
@@ -436,6 +620,18 @@ void FileMapInfo::write_header() {
|
||
align_file_position();
|
||
}
|
||
|
||
+void FileMapInfo::write_dynamic_header() {
|
||
+ align_file_position();
|
||
+ size_t sz = _header->data_size();
|
||
+ char* addr = _header->data();
|
||
+ write_bytes(addr, (int)sz); // skip the C++ vtable
|
||
+
|
||
+ char* base_archive_name = (char*)Arguments::GetSharedArchivePath();
|
||
+ if (base_archive_name != NULL) {
|
||
+ write_bytes(base_archive_name, dynamic_header()->base_archive_name_size());
|
||
+ }
|
||
+ align_file_position();
|
||
+}
|
||
|
||
// Dump shared spaces to file.
|
||
|
||
@@ -464,7 +660,15 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
|
||
} else {
|
||
si->_file_offset = _file_offset;
|
||
}
|
||
- si->_base = base;
|
||
+ if (is_static()) {
|
||
+ si->_base = base;
|
||
+ } else {
|
||
+ if (region == MetaspaceShared::d_bm) {
|
||
+ si->_base = NULL; // always NULL for bm region
|
||
+ } else {
|
||
+ si->_base = ArchiveBuilder::current()->to_requested(base);
|
||
+ }
|
||
+ }
|
||
si->_used = size;
|
||
si->_capacity = capacity;
|
||
si->_read_only = read_only;
|
||
@@ -473,7 +677,16 @@ void FileMapInfo::write_region(int region, char* base, size_t size,
|
||
write_bytes_aligned(base, (int)size);
|
||
}
|
||
|
||
+char* FileMapInfo::write_bitmap_region(const BitMap* ptrmap) {
|
||
+ size_t size_in_bits = ptrmap->size();
|
||
+ size_t size_in_bytes = ptrmap->size_in_words() * BytesPerWord;
|
||
+ char* buffer = NEW_C_HEAP_ARRAY(char, size_in_bytes, mtClassShared);
|
||
+ ptrmap->write_to((BitMap::bm_word_t*)buffer, size_in_bytes);
|
||
+ dynamic_header()->set_ptrmap_size_in_bits(size_in_bits);
|
||
|
||
+ write_region(MetaspaceShared::d_bm, (char*)buffer, size_in_bytes, size_in_bytes, /*read_only=*/true, /*allow_exec=*/false);
|
||
+ return buffer;
|
||
+}
|
||
// Dump bytes to file -- at the current file position.
|
||
|
||
void FileMapInfo::write_bytes(const void* buffer, int nbytes) {
|
||
@@ -542,7 +755,7 @@ void FileMapInfo::close() {
|
||
// JVM/TI RedefineClasses() support:
|
||
// Remap the shared readonly space to shared readwrite, private.
|
||
bool FileMapInfo::remap_shared_readonly_as_readwrite() {
|
||
- struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0];
|
||
+ struct FileMapInfo::FileMapHeader::space_info* si = is_static() ? &_header->_space[0] : &_header->_space[1];
|
||
if (!si->_read_only) {
|
||
// the space is already readwrite so we are done
|
||
return true;
|
||
@@ -570,10 +783,14 @@ bool FileMapInfo::remap_shared_readonly_as_readwrite() {
|
||
|
||
// Map the whole region at once, assumed to be allocated contiguously.
|
||
ReservedSpace FileMapInfo::reserve_shared_memory() {
|
||
- struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[0];
|
||
- char* requested_addr = si->_base;
|
||
+ char* requested_addr = region_base(0);
|
||
+ size_t size = 0;
|
||
|
||
- size_t size = FileMapInfo::shared_spaces_size();
|
||
+ if (is_static()) {
|
||
+ size = FileMapInfo::shared_spaces_size();
|
||
+ } else {
|
||
+ size = align_up((uintptr_t)region_end(1) - (uintptr_t)region_base(0), (size_t)os::vm_allocation_granularity());
|
||
+ }
|
||
|
||
// Reserve the space first, then map otherwise map will go right over some
|
||
// other reserved memory (like the code cache).
|
||
@@ -648,6 +865,7 @@ void FileMapInfo::assert_mark(bool check) {
|
||
|
||
|
||
FileMapInfo* FileMapInfo::_current_info = NULL;
|
||
+FileMapInfo* FileMapInfo::_dynamic_archive_info = NULL;
|
||
SharedClassPathEntry* FileMapInfo::_classpath_entry_table = NULL;
|
||
int FileMapInfo::_classpath_entry_table_size = 0;
|
||
size_t FileMapInfo::_classpath_entry_size = 0x1234baad;
|
||
@@ -674,19 +892,26 @@ bool FileMapInfo::initialize() {
|
||
if (!open_for_read()) {
|
||
return false;
|
||
}
|
||
-
|
||
- init_from_file(_fd);
|
||
+ if (!init_from_file(_fd)) {
|
||
+ return false;
|
||
+ }
|
||
if (!validate_header()) {
|
||
return false;
|
||
}
|
||
|
||
- SharedReadOnlySize = _header->_space[0]._capacity;
|
||
- SharedReadWriteSize = _header->_space[1]._capacity;
|
||
- SharedMiscDataSize = _header->_space[2]._capacity;
|
||
- SharedMiscCodeSize = _header->_space[3]._capacity;
|
||
+ if (is_static()) {
|
||
+ SharedReadOnlySize = _header->_space[0]._capacity;
|
||
+ SharedReadWriteSize = _header->_space[1]._capacity;
|
||
+ SharedMiscDataSize = _header->_space[2]._capacity;
|
||
+ SharedMiscCodeSize = _header->_space[3]._capacity;
|
||
+ }
|
||
return true;
|
||
}
|
||
|
||
+void FileMapInfo::DynamicArchiveHeader::set_as_offset(char* p, size_t *offset) {
|
||
+ *offset = ArchiveBuilder::current()->any_to_offset((address)p);
|
||
+}
|
||
+
|
||
int FileMapInfo::FileMapHeader::compute_crc() {
|
||
char* header = data();
|
||
// start computing from the field after _crc
|
||
@@ -701,7 +926,7 @@ int FileMapInfo::compute_header_crc() {
|
||
}
|
||
|
||
bool FileMapInfo::FileMapHeader::validate() {
|
||
- if (_magic != (int)0xf00baba2) {
|
||
+ if (_magic != CDS_ARCHIVE_MAGIC) {
|
||
FileMapInfo::fail_continue("The shared archive file has a bad magic number.");
|
||
return false;
|
||
}
|
||
@@ -738,6 +963,10 @@ bool FileMapInfo::FileMapHeader::validate() {
|
||
bool FileMapInfo::validate_header() {
|
||
bool status = _header->validate();
|
||
|
||
+ if (status && !is_static()) {
|
||
+ return DynamicArchive::validate(this);
|
||
+ }
|
||
+
|
||
if (status) {
|
||
if (!ClassLoader::check_shared_paths_misc_info(_paths_misc_info, _header->_paths_misc_info_size)) {
|
||
if (!PrintSharedArchiveAndExit) {
|
||
@@ -761,7 +990,13 @@ bool FileMapInfo::validate_header() {
|
||
// Return:
|
||
// True if the p is within the mapped shared space, otherwise, false.
|
||
bool FileMapInfo::is_in_shared_space(const void* p) {
|
||
- for (int i = 0; i < MetaspaceShared::n_regions; i++) {
|
||
+ int count = 0;
|
||
+ if (is_static()) {
|
||
+ count = MetaspaceShared::n_regions;
|
||
+ } else {
|
||
+ count = MetaspaceShared::d_n_regions;
|
||
+ }
|
||
+ for (int i = 0; i < count; i++) {
|
||
if (p >= _header->_space[i]._base &&
|
||
p < _header->_space[i]._base + _header->_space[i]._used) {
|
||
return true;
|
||
@@ -772,6 +1007,11 @@ bool FileMapInfo::is_in_shared_space(const void* p) {
|
||
}
|
||
|
||
void FileMapInfo::print_shared_spaces() {
|
||
+ // TODO: support dynamic archive
|
||
+ if (!is_static()) {
|
||
+ return;
|
||
+ }
|
||
+
|
||
gclog_or_tty->print_cr("Shared Spaces:");
|
||
for (int i = 0; i < MetaspaceShared::n_regions; i++) {
|
||
struct FileMapInfo::FileMapHeader::space_info* si = &_header->_space[i];
|
||
diff --git a/hotspot/src/share/vm/memory/filemap.hpp b/hotspot/src/share/vm/memory/filemap.hpp
|
||
index 0eee1c7ea..eab9ebcfc 100644
|
||
--- a/hotspot/src/share/vm/memory/filemap.hpp
|
||
+++ b/hotspot/src/share/vm/memory/filemap.hpp
|
||
@@ -27,6 +27,8 @@
|
||
|
||
#include "memory/metaspaceShared.hpp"
|
||
#include "memory/metaspace.hpp"
|
||
+#include "runtime/os.hpp"
|
||
+#include "utilities/align.hpp"
|
||
|
||
// Layout of the file:
|
||
// header: dump of archive instance plus versioning info, datestamp, etc.
|
||
@@ -37,8 +39,12 @@
|
||
// misc data (block offset table, string table, symbols, dictionary, etc.)
|
||
// tag(666)
|
||
|
||
+#define CDS_ARCHIVE_MAGIC 0xf00baba2
|
||
+#define CDS_DYNAMIC_ARCHIVE_MAGIC 0xf00baba8
|
||
+
|
||
static const int JVM_IDENT_MAX = 256;
|
||
|
||
+class BitMap;
|
||
class Metaspace;
|
||
|
||
class SharedClassPathEntry VALUE_OBJ_CLASS_SPEC {
|
||
@@ -56,11 +62,13 @@ private:
|
||
friend class ManifestStream;
|
||
enum {
|
||
_invalid_version = -1,
|
||
- _current_version = 2
|
||
+ _current_version = 3,
|
||
};
|
||
|
||
- bool _file_open;
|
||
- int _fd;
|
||
+ bool _is_static;
|
||
+ bool _file_open;
|
||
+ bool _is_mapped;
|
||
+ int _fd;
|
||
size_t _file_offset;
|
||
|
||
private:
|
||
@@ -77,20 +85,21 @@ public:
|
||
struct FileMapHeaderBase : public CHeapObj<mtClass> {
|
||
virtual bool validate() = 0;
|
||
virtual void populate(FileMapInfo* info, size_t alignment) = 0;
|
||
- };
|
||
- struct FileMapHeader : FileMapHeaderBase {
|
||
// Use data() and data_size() to memcopy to/from the FileMapHeader. We need to
|
||
// avoid read/writing the C++ vtable pointer.
|
||
- static size_t data_size();
|
||
+ virtual size_t data_size() = 0;
|
||
+ };
|
||
+ struct FileMapHeader : FileMapHeaderBase {
|
||
+ size_t data_size();
|
||
char* data() {
|
||
return ((char*)this) + sizeof(FileMapHeaderBase);
|
||
}
|
||
|
||
- int _magic; // identify file type.
|
||
- int _crc; // header crc checksum.
|
||
- int _version; // (from enum, above.)
|
||
- size_t _alignment; // how shared archive should be aligned
|
||
- int _obj_alignment; // value of ObjectAlignmentInBytes
|
||
+ unsigned int _magic; // identify file type.
|
||
+ int _crc; // header crc checksum.
|
||
+ int _version; // (from enum, above.)
|
||
+ size_t _alignment; // how shared archive should be aligned
|
||
+ int _obj_alignment; // value of ObjectAlignmentInBytes
|
||
|
||
struct space_info {
|
||
int _crc; // crc checksum of the current space
|
||
@@ -137,7 +146,48 @@ public:
|
||
|
||
virtual bool validate();
|
||
virtual void populate(FileMapInfo* info, size_t alignment);
|
||
+ int crc() { return _crc; }
|
||
+ int space_crc(int i) { return _space[i]._crc; }
|
||
int compute_crc();
|
||
+ unsigned int magic() const { return _magic; }
|
||
+ const char* jvm_ident() const { return _jvm_ident; }
|
||
+ };
|
||
+
|
||
+ // Fixme
|
||
+ struct DynamicArchiveHeader : FileMapHeader {
|
||
+ private:
|
||
+ int _base_header_crc;
|
||
+ int _base_region_crc[MetaspaceShared::n_regions];
|
||
+ char* _requested_base_address; // Archive relocation is not necessary if we map with this base address.
|
||
+ size_t _ptrmap_size_in_bits; // Size of pointer relocation bitmap
|
||
+ size_t _base_archive_name_size;
|
||
+ size_t _serialized_data_offset; // Data accessed using {ReadClosure,WriteClosure}::serialize()
|
||
+
|
||
+ public:
|
||
+ size_t data_size();
|
||
+ int base_header_crc() const { return _base_header_crc; }
|
||
+ int base_region_crc(int i) const {
|
||
+ return _base_region_crc[i];
|
||
+ }
|
||
+
|
||
+ void set_base_header_crc(int c) { _base_header_crc = c; }
|
||
+ void set_base_region_crc(int i, int c) {
|
||
+ _base_region_crc[i] = c;
|
||
+ }
|
||
+
|
||
+ void set_requested_base(char* b) {
|
||
+ _requested_base_address = b;
|
||
+ }
|
||
+ size_t ptrmap_size_in_bits() const { return _ptrmap_size_in_bits; }
|
||
+ void set_ptrmap_size_in_bits(size_t s) { _ptrmap_size_in_bits = s; }
|
||
+ void set_base_archive_name_size(size_t s) { _base_archive_name_size = s; }
|
||
+ size_t base_archive_name_size() { return _base_archive_name_size; }
|
||
+ void set_as_offset(char* p, size_t *offset);
|
||
+ char* from_mapped_offset(size_t offset) const { return _requested_base_address + offset; }
|
||
+ void set_serialized_data(char* p) { set_as_offset(p, &_serialized_data_offset); }
|
||
+ char* serialized_data() const { return from_mapped_offset(_serialized_data_offset); }
|
||
+
|
||
+ virtual bool validate();
|
||
};
|
||
|
||
FileMapHeader * _header;
|
||
@@ -147,32 +197,52 @@ public:
|
||
char* _paths_misc_info;
|
||
|
||
static FileMapInfo* _current_info;
|
||
+ static FileMapInfo* _dynamic_archive_info;
|
||
|
||
+ 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);
|
||
bool init_from_file(int fd);
|
||
void align_file_position();
|
||
bool validate_header_impl();
|
||
|
||
public:
|
||
- FileMapInfo();
|
||
+ FileMapInfo(bool is_static = true);
|
||
~FileMapInfo();
|
||
|
||
static int current_version() { return _current_version; }
|
||
int compute_header_crc();
|
||
void set_header_crc(int crc) { _header->_crc = crc; }
|
||
+ int space_crc(int i) { return _header->_space[i]._crc; }
|
||
void populate_header(size_t alignment);
|
||
bool validate_header();
|
||
void invalidate();
|
||
+ int crc() { return _header->_crc; }
|
||
int version() { return _header->_version; }
|
||
size_t alignment() { return _header->_alignment; }
|
||
size_t space_capacity(int i) { return _header->_space[i]._capacity; }
|
||
+ size_t used(int i) { return _header->_space[i]._used; }
|
||
+ size_t used_aligned(int i) { return align_up(used(i), (size_t)os::vm_allocation_granularity()); }
|
||
char* region_base(int i) { return _header->_space[i]._base; }
|
||
+ char* region_end(int i) { return region_base(i) + used_aligned(i); }
|
||
struct FileMapHeader* header() { return _header; }
|
||
+ struct DynamicArchiveHeader* dynamic_header() {
|
||
+ // assert(!is_static(), "must be");
|
||
+ return (struct DynamicArchiveHeader*)header();
|
||
+ }
|
||
+
|
||
+ void set_header_base_archive_name_size(size_t size) { dynamic_header()->set_base_archive_name_size(size); }
|
||
|
||
static FileMapInfo* current_info() {
|
||
CDS_ONLY(return _current_info;)
|
||
NOT_CDS(return NULL;)
|
||
}
|
||
|
||
+ static FileMapInfo* dynamic_info() {
|
||
+ CDS_ONLY(return _dynamic_archive_info;)
|
||
+ NOT_CDS(return NULL;)
|
||
+ }
|
||
+
|
||
static void assert_mark(bool check);
|
||
|
||
// File manipulation.
|
||
@@ -180,18 +250,24 @@ public:
|
||
bool open_for_read();
|
||
void open_for_write();
|
||
void write_header();
|
||
+ void write_dynamic_header();
|
||
void write_space(int i, Metaspace* space, bool read_only);
|
||
void write_region(int region, char* base, size_t size,
|
||
size_t capacity, bool read_only, bool allow_exec);
|
||
+ char* write_bitmap_region(const BitMap* ptrmap);
|
||
void write_bytes(const void* buffer, int count);
|
||
void write_bytes_aligned(const void* buffer, int count);
|
||
char* map_region(int i);
|
||
void unmap_region(int i);
|
||
bool verify_region_checksum(int i);
|
||
void close();
|
||
- bool is_open() { return _file_open; }
|
||
+ bool is_open() { return _file_open; }
|
||
+ bool is_static() const { return _is_static; }
|
||
+ bool is_mapped() const { return _is_mapped; }
|
||
+ void set_is_mapped(bool v) { _is_mapped = v; }
|
||
ReservedSpace reserve_shared_memory();
|
||
-
|
||
+ void set_requested_base(char* b) { dynamic_header()->set_requested_base(b); }
|
||
+ char* serialized_data() { return dynamic_header()->serialized_data(); }
|
||
// JVM/TI RedefineClasses() support:
|
||
// Remap the shared readonly space to shared readwrite, private.
|
||
bool remap_shared_readonly_as_readwrite();
|
||
diff --git a/hotspot/src/share/vm/memory/iterator.hpp b/hotspot/src/share/vm/memory/iterator.hpp
|
||
index 62204eea7..dc01186a2 100644
|
||
--- a/hotspot/src/share/vm/memory/iterator.hpp
|
||
+++ b/hotspot/src/share/vm/memory/iterator.hpp
|
||
@@ -378,6 +378,13 @@ public:
|
||
// for verification that sections of the serialized data are of the
|
||
// correct length.
|
||
virtual void do_tag(int tag) = 0;
|
||
+
|
||
+ // Read/write the 32-bit unsigned integer pointed to by p.
|
||
+ virtual void do_u4(u4* p) { }
|
||
+
|
||
+ bool writing() {
|
||
+ return !reading();
|
||
+ }
|
||
};
|
||
|
||
class SymbolClosure : public StackObj {
|
||
diff --git a/hotspot/src/share/vm/memory/metaspace.cpp b/hotspot/src/share/vm/memory/metaspace.cpp
|
||
index 2912f41b6..7e95b5c0b 100644
|
||
--- a/hotspot/src/share/vm/memory/metaspace.cpp
|
||
+++ b/hotspot/src/share/vm/memory/metaspace.cpp
|
||
@@ -37,6 +37,7 @@
|
||
#include "memory/metaspaceTracer.hpp"
|
||
#include "memory/resourceArea.hpp"
|
||
#include "memory/universe.hpp"
|
||
+#include "runtime/arguments.hpp"
|
||
#include "runtime/atomic.inline.hpp"
|
||
#include "runtime/globals.hpp"
|
||
#include "runtime/init.hpp"
|
||
@@ -426,8 +427,16 @@ VirtualSpaceNode::VirtualSpaceNode(size_t bytes) : _top(NULL), _next(NULL), _rs(
|
||
assert(shared_base == 0 || _rs.base() == shared_base, "should match");
|
||
} else {
|
||
// Get a mmap region anywhere if the SharedBaseAddress fails.
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Could not allocate static space at request address: " INTPTR_FORMAT, p2i(shared_base));
|
||
+ }
|
||
_rs = ReservedSpace(bytes, Metaspace::reserve_alignment(), large_pages);
|
||
}
|
||
+ // ...failing that, give up.
|
||
+ if (!_rs.is_reserved()) {
|
||
+ vm_exit_during_initialization(
|
||
+ err_msg("Could not allocate static shared space: " SIZE_FORMAT " bytes", bytes));
|
||
+ }
|
||
MetaspaceShared::set_shared_rs(&_rs);
|
||
} else
|
||
#endif
|
||
@@ -3322,21 +3331,80 @@ void Metaspace::global_initialize() {
|
||
// the addresses don't conflict)
|
||
address cds_address = NULL;
|
||
if (UseSharedSpaces) {
|
||
- FileMapInfo* mapinfo = new FileMapInfo();
|
||
+ FileMapInfo* static_mapinfo = new FileMapInfo();
|
||
+ FileMapInfo* dynamic_mapinfo = new FileMapInfo(false);
|
||
|
||
// 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() && MetaspaceShared::map_shared_spaces(mapinfo)) {
|
||
+ //
|
||
+ // This will reserve two address spaces suitable to house Klass structures, one
|
||
+ // for the cds archives (static archive and optionally dynamic archive) and
|
||
+ // optionally one move for ccs.
|
||
+ //
|
||
+ // Since both spaces must fall within the compressed class pointer encoding
|
||
+ // range, they are allocated close to each other.
|
||
+ //
|
||
+ // Space for archives will be reserved first, followed by a potential gap,
|
||
+ // followed by the space for ccs:
|
||
+ //
|
||
+ // +-- Base address End
|
||
+ // | |
|
||
+ // v v
|
||
+ // +------------+ +-------------+ +--------------------+
|
||
+ // | static arc | [align] | [dyn. arch] | [align] | compr. class space |
|
||
+ // +------------+ +-------------+ +--------------------+
|
||
+ //
|
||
+ // (The gap may result from different alignment requirements between metaspace
|
||
+ // and CDS)
|
||
+ //
|
||
+ // If UseCompressedClassPointers is disabled, only one address space will be
|
||
+ // reserved:
|
||
+ //
|
||
+ // +-- Base address End
|
||
+ // | |
|
||
+ // v v
|
||
+ // +------------+ +-------------+
|
||
+ // | static arc | [align] | [dyn. arch] |
|
||
+ // +------------+ +-------------+
|
||
+ //
|
||
+ // If UseCompressedClassPointers=1, the range encompassing both spaces will be
|
||
+ // suitable to en/decode narrow Klass pointers: the base will be valid for
|
||
+ // encoding, the range [Base, End) not surpass KlassEncodingMetaspaceMax.
|
||
+ if (static_mapinfo->initialize() && MetaspaceShared::map_shared_spaces(static_mapinfo)) {
|
||
cds_total = FileMapInfo::shared_spaces_size();
|
||
- cds_address = (address)mapinfo->region_base(0);
|
||
+ cds_address = (address)static_mapinfo->region_base(0);
|
||
+ MetaspaceShared::set_shared_metaspace_static_bottom(cds_address);
|
||
+ // Update SharedBaseAddress to the same value as the dump phase.
|
||
+ SharedBaseAddress = (size_t)cds_address;
|
||
+ if (!DynamicDumpSharedSpaces &&
|
||
+ (Arguments::GetSharedDynamicArchivePath() != NULL) &&
|
||
+ dynamic_mapinfo->initialize() &&
|
||
+ MetaspaceShared::map_shared_spaces(dynamic_mapinfo)) {
|
||
+ cds_total += align_up(dynamic_mapinfo->region_end(1) - dynamic_mapinfo->region_base(0),
|
||
+ (size_t)os::vm_allocation_granularity());
|
||
+ } else {
|
||
+ assert(!dynamic_mapinfo->is_open(),
|
||
+ "dynamic archive file not closed or shared spaces not disabled.");
|
||
+ }
|
||
} else {
|
||
- assert(!mapinfo->is_open() && !UseSharedSpaces,
|
||
- "archive file not closed or shared spaces not disabled.");
|
||
+ assert(!static_mapinfo->is_open() && !UseSharedSpaces,
|
||
+ "static archive file not closed or shared spaces not disabled.");
|
||
+ }
|
||
+
|
||
+ if (static_mapinfo != NULL && !static_mapinfo->is_mapped()) {
|
||
+ delete static_mapinfo;
|
||
+ }
|
||
+ if (dynamic_mapinfo != NULL && !dynamic_mapinfo->is_mapped()) {
|
||
+ delete dynamic_mapinfo;
|
||
}
|
||
}
|
||
+
|
||
+ if (DynamicDumpSharedSpaces && !UseSharedSpaces) {
|
||
+ vm_exit_during_initialization("DynamicDumpSharedSpaces is unsupported when base CDS archive is not loaded", NULL);
|
||
+ }
|
||
#endif // INCLUDE_CDS
|
||
+
|
||
#ifdef _LP64
|
||
// If UseCompressedClassPointers is set then allocate the metaspace area
|
||
// above the heap and above the CDS area (if it exists).
|
||
diff --git a/hotspot/src/share/vm/memory/metaspace.hpp b/hotspot/src/share/vm/memory/metaspace.hpp
|
||
index 3920004a8..2b06cb620 100644
|
||
--- a/hotspot/src/share/vm/memory/metaspace.hpp
|
||
+++ b/hotspot/src/share/vm/memory/metaspace.hpp
|
||
@@ -82,6 +82,7 @@ class VirtualSpaceList;
|
||
// quantum of metadata.
|
||
|
||
class Metaspace : public CHeapObj<mtClass> {
|
||
+ friend class ArchiveBuilder;
|
||
friend class VMStructs;
|
||
friend class SpaceManager;
|
||
friend class VM_CollectForMetadataAllocation;
|
||
diff --git a/hotspot/src/share/vm/memory/metaspaceClosure.cpp b/hotspot/src/share/vm/memory/metaspaceClosure.cpp
|
||
new file mode 100644
|
||
index 000000000..00ec8fced
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/memory/metaspaceClosure.cpp
|
||
@@ -0,0 +1,87 @@
|
||
+#include "precompiled.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
+
|
||
+// Update the reference to point to new_loc.
|
||
+void MetaspaceClosure::Ref::update(address new_loc) const {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Ref: [" PTR_FORMAT "] -> " PTR_FORMAT " => " PTR_FORMAT,
|
||
+ p2i(mpp()), p2i(obj()), p2i(new_loc));
|
||
+ }
|
||
+ uintx p = (uintx)new_loc;
|
||
+ p |= flag_bits(); // Make sure the flag bits are copied to the new pointer.
|
||
+ *(address*)mpp() = (address)p;
|
||
+}
|
||
+
|
||
+void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) {
|
||
+ if (_nest_level < MAX_NEST_LEVEL) {
|
||
+ do_push(ref);
|
||
+ if (!ref->keep_after_pushing()) {
|
||
+ delete ref;
|
||
+ }
|
||
+ } else {
|
||
+ do_pending_ref(ref);
|
||
+ ref->set_next(_pending_refs);
|
||
+ _pending_refs = ref;
|
||
+ }
|
||
+}
|
||
+
|
||
+void MetaspaceClosure::do_push(MetaspaceClosure::Ref* ref) {
|
||
+ if (ref->not_null()) {
|
||
+ bool read_only;
|
||
+ Writability w = ref->writability();
|
||
+ switch (w) {
|
||
+ case _writable:
|
||
+ read_only = false;
|
||
+ break;
|
||
+ case _not_writable:
|
||
+ read_only = true;
|
||
+ break;
|
||
+ default:
|
||
+ assert(w == _default, "must be");
|
||
+ read_only = ref->is_read_only_by_default();
|
||
+ }
|
||
+ if (_nest_level == 0) {
|
||
+ assert(_enclosing_ref == NULL, "must be");
|
||
+ }
|
||
+ _nest_level ++;
|
||
+ if (do_ref(ref, read_only)) { // true means we want to iterate the embedded pointer in <ref>
|
||
+ Ref* saved = _enclosing_ref;
|
||
+ _enclosing_ref = ref;
|
||
+ ref->metaspace_pointers_do(this);
|
||
+ _enclosing_ref = saved;
|
||
+ }
|
||
+ _nest_level --;
|
||
+ }
|
||
+}
|
||
+
|
||
+void MetaspaceClosure::finish() {
|
||
+ assert(_nest_level == 0, "must be");
|
||
+ while (_pending_refs != NULL) {
|
||
+ Ref* ref = _pending_refs;
|
||
+ _pending_refs = _pending_refs->next();
|
||
+ do_push(ref);
|
||
+ if (!ref->keep_after_pushing()) {
|
||
+ delete ref;
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+MetaspaceClosure::~MetaspaceClosure() {
|
||
+ assert(_pending_refs == NULL,
|
||
+ "you must explicitly call MetaspaceClosure::finish() to process all refs!");
|
||
+}
|
||
+
|
||
+bool UniqueMetaspaceClosure::do_ref(MetaspaceClosure::Ref* ref, bool read_only) {
|
||
+ bool created;
|
||
+ _has_been_visited.add_if_absent(ref->obj(), read_only, &created);
|
||
+ if (!created) {
|
||
+ return false; // Already visited: no need to iterate embedded pointers.
|
||
+ } else {
|
||
+ if (_has_been_visited.maybe_grow(MAX_TABLE_SIZE)) {
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Expanded _has_been_visited table to %d", _has_been_visited.table_size());
|
||
+ }
|
||
+ }
|
||
+ return do_unique_ref(ref, read_only);
|
||
+ }
|
||
+}
|
||
diff --git a/hotspot/src/share/vm/memory/metaspaceClosure.hpp b/hotspot/src/share/vm/memory/metaspaceClosure.hpp
|
||
new file mode 100644
|
||
index 000000000..f67d8d6fd
|
||
--- /dev/null
|
||
+++ b/hotspot/src/share/vm/memory/metaspaceClosure.hpp
|
||
@@ -0,0 +1,381 @@
|
||
+
|
||
+
|
||
+#ifndef SHARE_VM_MEMORY_METASPACECLOSURE_HPP
|
||
+#define SHARE_VM_MEMORY_METASPACECLOSURE_HPP
|
||
+
|
||
+#include "memory/allocation.hpp"
|
||
+#include "utilities/array.hpp"
|
||
+#include "utilities/globalDefinitions.hpp"
|
||
+#include "utilities/hashtable.hpp"
|
||
+
|
||
+// The metadata hierarchy is separate from the oop hierarchy
|
||
+ class MetaspaceObj; // no C++ vtable
|
||
+//class Array; // no C++ vtable
|
||
+ class Annotations; // no C++ vtable
|
||
+ class ConstantPoolCache; // no C++ vtable
|
||
+ class ConstMethod; // no C++ vtable
|
||
+ class MethodCounters; // no C++ vtable
|
||
+ class Symbol; // no C++ vtable
|
||
+ class Metadata; // has C++ vtable (so do all subclasses)
|
||
+ class ConstantPool;
|
||
+ class MethodData;
|
||
+ class Method;
|
||
+ class Klass;
|
||
+ class InstanceKlass;
|
||
+ class InstanceMirrorKlass;
|
||
+ class InstanceClassLoaderKlass;
|
||
+ class InstanceRefKlass;
|
||
+ class ArrayKlass;
|
||
+ class ObjArrayKlass;
|
||
+ class TypeArrayKlass;
|
||
+
|
||
+// class MetaspaceClosure --
|
||
+//
|
||
+// This class is used for iterating the objects in the HotSpot Metaspaces. It
|
||
+// provides an API to walk all the reachable objects starting from a set of
|
||
+// root references (such as all Klass'es in the SystemDictionary).
|
||
+//
|
||
+// Currently it is used for compacting the CDS archive by eliminate temporary
|
||
+// objects allocated during archive creation time. See ArchiveBuilder for an example.
|
||
+//
|
||
+// To support MetaspaceClosure, each subclass of MetaspaceObj must provide
|
||
+// a method of the type void metaspace_pointers_do(MetaspaceClosure*). This method
|
||
+// should call MetaspaceClosure::push() on every pointer fields of this
|
||
+// class that points to a MetaspaceObj. See Annotations::metaspace_pointers_do()
|
||
+// for an example.
|
||
+
|
||
+
|
||
+class MetaspaceClosure : public StackObj {
|
||
+public:
|
||
+ enum Writability {
|
||
+ _writable,
|
||
+ _not_writable,
|
||
+ _default
|
||
+ };
|
||
+
|
||
+ enum SpecialRef {
|
||
+ _method_entry_ref
|
||
+ };
|
||
+
|
||
+ // class MetaspaceClosure::Ref --
|
||
+ //
|
||
+ // MetaspaceClosure can be viewed as a very simple type of copying garbage
|
||
+ // collector. For it to function properly, it requires each subclass of
|
||
+ // MetaspaceObj to provide two methods:
|
||
+ //
|
||
+ // size_t size(); -- to determine how much data to copy
|
||
+ // void metaspace_pointers_do(MetaspaceClosure*); -- to locate all the embedded pointers
|
||
+ //
|
||
+ // Calling these methods would be trivial if these two were virtual methods.
|
||
+ // However, to save space, MetaspaceObj has NO vtable. The vtable is introduced
|
||
+ // only in the Metadata class.
|
||
+ //
|
||
+ // To work around the lack of a vtable, we use the Ref class with templates
|
||
+ // (see MSORef, OtherArrayRef, MSOArrayRef, and MSOPointerArrayRef)
|
||
+ // so that we can statically discover the type of a object. The use of Ref
|
||
+ // depends on the fact that:
|
||
+ //
|
||
+ // [1] We don't use polymorphic pointers for MetaspaceObj's that are not subclasses
|
||
+ // of Metadata. I.e., we don't do this:
|
||
+ // class Klass {
|
||
+ // MetaspaceObj *_obj;
|
||
+ // Array<int>* foo() { return (Array<int>*)_obj; }
|
||
+ // Symbol* bar() { return (Symbol*) _obj; }
|
||
+ //
|
||
+ // [2] All Array<T> dimensions are statically declared.
|
||
+ class Ref : public CHeapObj<mtClassShared> {
|
||
+ Writability _writability;
|
||
+ bool _keep_after_pushing;
|
||
+ Ref* _next;
|
||
+ void* _user_data;
|
||
+
|
||
+ protected:
|
||
+ virtual void** mpp() const = 0;
|
||
+ Ref(Writability w) : _writability(w), _keep_after_pushing(false), _next(NULL), _user_data(NULL) {}
|
||
+ public:
|
||
+ virtual bool not_null() const = 0;
|
||
+ virtual int size() const = 0;
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure *it) const = 0;
|
||
+ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const = 0;
|
||
+ virtual MetaspaceObj::Type msotype() const = 0;
|
||
+ virtual bool is_read_only_by_default() const = 0;
|
||
+ virtual ~Ref() {}
|
||
+
|
||
+ address obj() const {
|
||
+ // In some rare cases (see CPSlot in constantPool.hpp) we store some flags in the lowest
|
||
+ // 2 bits of a MetaspaceObj pointer. Unmask these when manipulating the pointer.
|
||
+ uintx p = (uintx)*mpp();
|
||
+ return (address)(p & (~FLAG_MASK));
|
||
+ }
|
||
+
|
||
+ address* addr() const {
|
||
+ return (address*)mpp();
|
||
+ }
|
||
+
|
||
+ void update(address new_loc) const;
|
||
+
|
||
+ Writability writability() const { return _writability; };
|
||
+ void set_keep_after_pushing() { _keep_after_pushing = true; }
|
||
+ bool keep_after_pushing() { return _keep_after_pushing; }
|
||
+ void set_user_data(void* data) { _user_data = data; }
|
||
+ void* user_data() { return _user_data; }
|
||
+ void set_next(Ref* n) { _next = n; }
|
||
+ Ref* next() const { return _next; }
|
||
+
|
||
+ private:
|
||
+ static const uintx FLAG_MASK = 0x03;
|
||
+
|
||
+ int flag_bits() const {
|
||
+ uintx p = (uintx)*mpp();
|
||
+ return (int)(p & FLAG_MASK);
|
||
+ }
|
||
+ };
|
||
+
|
||
+private:
|
||
+ // MSORef -- iterate an instance of MetaspaceObj
|
||
+ template <class T> class MSORef : public Ref {
|
||
+ T** _mpp;
|
||
+ T* dereference() const {
|
||
+ return *_mpp;
|
||
+ }
|
||
+ protected:
|
||
+ virtual void** mpp() const {
|
||
+ return (void**)_mpp;
|
||
+ }
|
||
+
|
||
+ public:
|
||
+ MSORef(T** mpp, Writability w) : Ref(w), _mpp(mpp) {}
|
||
+
|
||
+ virtual bool is_read_only_by_default() const { return T::is_read_only_by_default(); }
|
||
+ virtual bool not_null() const { return dereference() != NULL; }
|
||
+ virtual int size() const { return dereference()->size(); }
|
||
+ virtual MetaspaceObj::Type msotype() const { return dereference()->type(); }
|
||
+
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
|
||
+ dereference()->metaspace_pointers_do(it);
|
||
+ }
|
||
+ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
|
||
+ ((T*)new_loc)->metaspace_pointers_do(it);
|
||
+ }
|
||
+ };
|
||
+
|
||
+ // abstract base class for MSOArrayRef, MSOPointerArrayRef and OtherArrayRef
|
||
+ template <class T> class ArrayRef : public Ref {
|
||
+ Array<T>** _mpp;
|
||
+ protected:
|
||
+ Array<T>* dereference() const {
|
||
+ return *_mpp;
|
||
+ }
|
||
+ virtual void** mpp() const {
|
||
+ return (void**)_mpp;
|
||
+ }
|
||
+
|
||
+ ArrayRef(Array<T>** mpp, Writability w) : Ref(w), _mpp(mpp) {}
|
||
+
|
||
+ // all Arrays are read-only by default
|
||
+ virtual bool is_read_only_by_default() const { return true; }
|
||
+ virtual bool not_null() const { return dereference() != NULL; }
|
||
+ virtual int size() const { return dereference()->size(); }
|
||
+ virtual MetaspaceObj::Type msotype() const { return MetaspaceObj::array_type(sizeof(T)); }
|
||
+ };
|
||
+
|
||
+ // OtherArrayRef -- iterate an instance of Array<T>, where T is NOT a subtype of MetaspaceObj.
|
||
+ // T can be a primitive type, such as int, or a structure. However, we do not scan
|
||
+ // the fields inside T, so you should not embed any pointers inside T.
|
||
+ template <class T> class OtherArrayRef : public ArrayRef<T> {
|
||
+ public:
|
||
+ OtherArrayRef(Array<T>** mpp, Writability w) : ArrayRef<T>(mpp, w) {}
|
||
+
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
|
||
+ Array<T>* array = ArrayRef<T>::dereference();
|
||
+ if (TraceDynamicCDS)
|
||
+ dynamic_cds_log->print_cr("Iter(OtherArray): %p [%d]", array, array->length());
|
||
+ }
|
||
+ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
|
||
+ Array<T>* array = (Array<T>*)new_loc;
|
||
+ if (TraceDynamicCDS)
|
||
+ dynamic_cds_log->print_cr("Iter(OtherArray): %p [%d]", array, array->length());
|
||
+ }
|
||
+ };
|
||
+
|
||
+ // MSOArrayRef -- iterate an instance of Array<T>, where T is a subtype of MetaspaceObj.
|
||
+ // We recursively call T::metaspace_pointers_do() for each element in this array.
|
||
+ template <class T> class MSOArrayRef : public ArrayRef<T> {
|
||
+ public:
|
||
+ MSOArrayRef(Array<T>** mpp, Writability w) : ArrayRef<T>(mpp, w) {}
|
||
+
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
|
||
+ metaspace_pointers_do_at_impl(it, ArrayRef<T>::dereference());
|
||
+ }
|
||
+ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
|
||
+ metaspace_pointers_do_at_impl(it, (Array<T>*)new_loc);
|
||
+ }
|
||
+ private:
|
||
+ void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array<T>* array) const {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Iter(MSOArray): %p [%d]", array, array->length());
|
||
+ }
|
||
+ for (int i = 0; i < array->length(); i++) {
|
||
+ T* elm = array->adr_at(i);
|
||
+ elm->metaspace_pointers_do(it);
|
||
+ }
|
||
+ }
|
||
+ };
|
||
+
|
||
+ // MSOPointerArrayRef -- iterate an instance of Array<T*>, where T is a subtype of MetaspaceObj.
|
||
+ // We recursively call MetaspaceClosure::push() for each pointer in this array.
|
||
+ template <class T> class MSOPointerArrayRef : public ArrayRef<T*> {
|
||
+ public:
|
||
+ MSOPointerArrayRef(Array<T*>** mpp, Writability w) : ArrayRef<T*>(mpp, w) {}
|
||
+
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
|
||
+ metaspace_pointers_do_at_impl(it, ArrayRef<T*>::dereference());
|
||
+ }
|
||
+ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
|
||
+ metaspace_pointers_do_at_impl(it, (Array<T*>*)new_loc);
|
||
+ }
|
||
+ private:
|
||
+ void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array<T*>* array) const {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Iter(MSOPointerArray): %p [%d]", array, array->length());
|
||
+ }
|
||
+ for (int i = 0; i < array->length(); i++) {
|
||
+ T** mpp = array->adr_at(i);
|
||
+ it->push(mpp);
|
||
+ }
|
||
+ }
|
||
+ };
|
||
+
|
||
+ // Normally, chains of references like a->b->c->d are iterated recursively. However,
|
||
+ // if recursion is too deep, we save the Refs in _pending_refs, and push them later in
|
||
+ // MetaspaceClosure::finish(). This avoids overflowing the C stack.
|
||
+ static const int MAX_NEST_LEVEL = 5;
|
||
+ Ref* _pending_refs;
|
||
+ int _nest_level;
|
||
+ Ref* _enclosing_ref;
|
||
+
|
||
+ void push_impl(Ref* ref);
|
||
+ void do_push(Ref* ref);
|
||
+
|
||
+public:
|
||
+ MetaspaceClosure(): _pending_refs(NULL), _nest_level(0), _enclosing_ref(NULL) {}
|
||
+ ~MetaspaceClosure();
|
||
+
|
||
+ void finish();
|
||
+
|
||
+ // enclosing_ref() is used to compute the offset of a field in a C++ class. For example
|
||
+ // class Foo { intx scala; Bar* ptr; }
|
||
+ // Foo *f = 0x100;
|
||
+ // when the f->ptr field is iterated with do_ref() on 64-bit platforms, we will have
|
||
+ // do_ref(Ref* r) {
|
||
+ // r->addr() == 0x108; // == &f->ptr;
|
||
+ // enclosing_ref()->obj() == 0x100; // == foo
|
||
+ // So we know that we are iterating upon a field at offset 8 of the object at 0x100.
|
||
+ //
|
||
+ // Note that if we have stack overflow, do_pending_ref(r) will be called first and
|
||
+ // do_ref(r) will be called later, for the same r. In this case, enclosing_ref() is valid only
|
||
+ // when do_pending_ref(r) is called, and will return NULL when do_ref(r) is called.
|
||
+ Ref* enclosing_ref() const {
|
||
+ return _enclosing_ref;
|
||
+ }
|
||
+
|
||
+ // This is called when a reference is placed in _pending_refs. Override this
|
||
+ // function if you're using enclosing_ref(). See notes above.
|
||
+ virtual void do_pending_ref(Ref* ref) {}
|
||
+
|
||
+ // returns true if we want to keep iterating the pointers embedded inside <ref>
|
||
+ virtual bool do_ref(Ref* ref, bool read_only) = 0;
|
||
+
|
||
+private:
|
||
+ template <class REF_TYPE, typename T>
|
||
+ void push_with_ref(T** mpp, Writability w) {
|
||
+ push_impl(new REF_TYPE(mpp, w));
|
||
+ }
|
||
+
|
||
+public:
|
||
+ // When MetaspaceClosure::push(...) is called, pick the correct Ref subtype to handle it:
|
||
+ //
|
||
+ // MetaspaceClosure* it = ...;
|
||
+ // Klass* o = ...; it->push(&o); => MSORef
|
||
+ // Array<int>* a1 = ...; it->push(&a1); => OtherArrayRef
|
||
+ // Array<Annotation>* a2 = ...; it->push(&a2); => MSOArrayRef
|
||
+ // Array<Klass*>* a3 = ...; it->push(&a3); => MSOPointerArrayRef
|
||
+ // Array<Array<Klass*>*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef
|
||
+ // Array<Annotation*>* a5 = ...; it->push(&a5); => MSOPointerArrayRef
|
||
+ //
|
||
+ // Note that the following will fail to compile (to prevent you from adding new fields
|
||
+ // into the MetaspaceObj subtypes that cannot be properly copied by CDS):
|
||
+ //
|
||
+ // Hashtable* h = ...; it->push(&h); => Hashtable is not a subclass of MetaspaceObj
|
||
+ // Array<Hashtable*>* a6 = ...; it->push(&a6); => Hashtable is not a subclass of MetaspaceObj
|
||
+ // Array<int*>* a7 = ...; it->push(&a7); => int is not a subclass of MetaspaceObj
|
||
+
|
||
+ template <typename T>
|
||
+ void push(T** mpp, Writability w = _default) {
|
||
+ push_with_ref<MSORef<T> >(mpp, w);
|
||
+ }
|
||
+
|
||
+ void push(Array<u1>** mpp, Writability w = _default) {
|
||
+ push_with_ref<OtherArrayRef<u1> >(mpp, w);
|
||
+ }
|
||
+
|
||
+ void push(Array<u2>** mpp, Writability w = _default) {
|
||
+ push_with_ref<OtherArrayRef<u2> >(mpp, w);
|
||
+ }
|
||
+
|
||
+ void push(Array<u4>** mpp, Writability w = _default) {
|
||
+ push_with_ref<OtherArrayRef<u4> >(mpp, w);
|
||
+ }
|
||
+
|
||
+ void push(Array<u8>** mpp, Writability w = _default) {
|
||
+ push_with_ref<OtherArrayRef<u8> >(mpp, w);
|
||
+ }
|
||
+
|
||
+ void push(Array<int>** mpp, Writability w = _default) {
|
||
+ push_with_ref<OtherArrayRef<int> >(mpp, w);
|
||
+ }
|
||
+
|
||
+ template <typename T>
|
||
+ void push(Array<T>** mpp, Writability w = _default) {
|
||
+ push_with_ref<MSOArrayRef<T> >(mpp, w);
|
||
+ }
|
||
+
|
||
+ template <typename T>
|
||
+ void push(Array<T*>** mpp, Writability w = _default) {
|
||
+ push_with_ref<MSOPointerArrayRef<T> >(mpp, w);
|
||
+ }
|
||
+
|
||
+#if 0
|
||
+ // Enable this block if you're changing the push(...) methods, to test for types that should be
|
||
+ // disallowed. Each of the following "push" calls should result in a compile-time error.
|
||
+ void test_disallowed_types(MetaspaceClosure* it) {
|
||
+ Hashtable<bool, mtInternal>* h = NULL;
|
||
+ it->push(&h);
|
||
+
|
||
+ Array<Hashtable<bool, mtInternal>*>* a6 = NULL;
|
||
+ it->push(&a6);
|
||
+
|
||
+ Array<int*>* a7 = NULL;
|
||
+ it->push(&a7);
|
||
+ }
|
||
+#endif
|
||
+};
|
||
+
|
||
+// This is a special MetaspaceClosure that visits each unique MetaspaceObj once.
|
||
+class UniqueMetaspaceClosure : public MetaspaceClosure {
|
||
+ static const int INITIAL_TABLE_SIZE = 15889;
|
||
+ static const int MAX_TABLE_SIZE = 1000000;
|
||
+
|
||
+ // Do not override. Returns true if we are discovering ref->obj() for the first time.
|
||
+ virtual bool do_ref(Ref* ref, bool read_only);
|
||
+
|
||
+public:
|
||
+ // Gets called the first time we discover an object.
|
||
+ virtual bool do_unique_ref(Ref* ref, bool read_only) = 0;
|
||
+ UniqueMetaspaceClosure() : _has_been_visited(INITIAL_TABLE_SIZE) {}
|
||
+
|
||
+private:
|
||
+ KVHashtable<address, bool, mtInternal> _has_been_visited;
|
||
+};
|
||
+
|
||
+#endif // SHARE_MEMORY_METASPACECLOSURE_HPP
|
||
diff --git a/hotspot/src/share/vm/memory/metaspaceShared.cpp b/hotspot/src/share/vm/memory/metaspaceShared.cpp
|
||
index 9857b7577..00fb9fe91 100644
|
||
--- a/hotspot/src/share/vm/memory/metaspaceShared.cpp
|
||
+++ b/hotspot/src/share/vm/memory/metaspaceShared.cpp
|
||
@@ -38,6 +38,7 @@
|
||
#include "memory/metaspaceShared.hpp"
|
||
#include "oops/objArrayOop.hpp"
|
||
#include "oops/oop.inline.hpp"
|
||
+#include "runtime/arguments.hpp"
|
||
#include "runtime/signature.hpp"
|
||
#include "runtime/vm_operations.hpp"
|
||
#include "runtime/vmThread.hpp"
|
||
@@ -47,14 +48,17 @@
|
||
PRAGMA_FORMAT_MUTE_WARNINGS_FOR_GCC
|
||
|
||
int MetaspaceShared::_max_alignment = 0;
|
||
-
|
||
ReservedSpace* MetaspaceShared::_shared_rs = NULL;
|
||
+char* MetaspaceShared::_requested_base_address;
|
||
|
||
bool MetaspaceShared::_link_classes_made_progress;
|
||
bool MetaspaceShared::_check_classes_made_progress;
|
||
bool MetaspaceShared::_has_error_classes;
|
||
bool MetaspaceShared::_archive_loading_failed = false;
|
||
bool MetaspaceShared::_remapped_readwrite = false;
|
||
+void* MetaspaceShared::_shared_metaspace_static_bottom = NULL;
|
||
+void* MetaspaceShared::_shared_metaspace_dynamic_base = NULL;
|
||
+void* MetaspaceShared::_shared_metaspace_dynamic_top = NULL;
|
||
// Read/write a data stream for restoring/preserving metadata pointers and
|
||
// miscellaneous data from/to the shared archive file.
|
||
|
||
@@ -843,7 +847,7 @@ int MetaspaceShared::preload_and_dump(const char * class_list_path,
|
||
|
||
// Returns true if the class's status has changed
|
||
bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) {
|
||
- assert(DumpSharedSpaces, "should only be called during dumping");
|
||
+// assert(DumpSharedSpaces, "should only be called during dumping");
|
||
if (ik->init_state() < InstanceKlass::linked) {
|
||
bool saved = BytecodeVerificationLocal;
|
||
if (!SharedClassUtil::is_shared_boot_class(ik)) {
|
||
@@ -862,6 +866,7 @@ bool MetaspaceShared::try_link_class(InstanceKlass* ik, TRAPS) {
|
||
tty->print_cr("Preload Warning: Verification failed for %s",
|
||
ik->external_name());
|
||
CLEAR_PENDING_EXCEPTION;
|
||
+ SystemDictionaryShared::set_class_has_failed_verification(ik);
|
||
ik->set_in_error_state();
|
||
_has_error_classes = true;
|
||
}
|
||
@@ -902,6 +907,11 @@ public:
|
||
FileMapInfo::assert_mark(tag == old_tag);
|
||
}
|
||
|
||
+ void do_u4(u4* p) {
|
||
+ intptr_t obj = nextPtr();
|
||
+ *p = (u4)(uintx(obj));
|
||
+ }
|
||
+
|
||
void do_region(u_char* start, size_t size) {
|
||
assert((intptr_t)start % sizeof(intptr_t) == 0, "bad alignment");
|
||
assert(size % sizeof(intptr_t) == 0, "bad size");
|
||
@@ -918,7 +928,10 @@ public:
|
||
|
||
// Return true if given address is in the mapped shared space.
|
||
bool MetaspaceShared::is_in_shared_space(const void* p) {
|
||
- return UseSharedSpaces && FileMapInfo::current_info()->is_in_shared_space(p);
|
||
+ return UseSharedSpaces && ((FileMapInfo::current_info() != NULL &&
|
||
+ FileMapInfo::current_info()->is_mapped() &&
|
||
+ FileMapInfo::current_info()->is_in_shared_space(p)) ||
|
||
+ is_shared_dynamic(p));
|
||
}
|
||
|
||
void MetaspaceShared::print_shared_spaces() {
|
||
@@ -927,19 +940,34 @@ void MetaspaceShared::print_shared_spaces() {
|
||
}
|
||
}
|
||
|
||
-
|
||
// Map shared spaces at requested addresses and return if succeeded.
|
||
// Need to keep the bounds of the ro and rw space for the Metaspace::contains
|
||
// call, or is_in_shared_space.
|
||
bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
|
||
size_t image_alignment = mapinfo->alignment();
|
||
|
||
+ mapinfo->set_is_mapped(false);
|
||
+
|
||
#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;
|
||
+ if (!shared_rs.is_reserved()) {
|
||
+ FileMapInfo::fail_continue("Unable to reserve shared memory");
|
||
+ FLAG_SET_DEFAULT(UseSharedSpaces, false);
|
||
+ return false;
|
||
+ }
|
||
+ if (InfoDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Reserved archive_space_rs [" INTPTR_FORMAT " - " INTPTR_FORMAT "] (" SIZE_FORMAT ") bytes",
|
||
+ p2i(shared_rs.base()), p2i(shared_rs.base() + shared_rs.size()), shared_rs.size());
|
||
+ }
|
||
+ if (mapinfo->is_static()) {
|
||
+ _requested_base_address = shared_rs.base();
|
||
+ } else {
|
||
+ _shared_metaspace_dynamic_base = shared_rs.base();
|
||
+ _shared_metaspace_dynamic_top = shared_rs.base() + shared_rs.size();
|
||
+ }
|
||
#endif
|
||
|
||
assert(!DumpSharedSpaces, "Should not be called with DumpSharedSpaces");
|
||
@@ -950,40 +978,79 @@ bool MetaspaceShared::map_shared_spaces(FileMapInfo* mapinfo) {
|
||
char* _mc_base = NULL;
|
||
|
||
// Map each shared region
|
||
- if ((_ro_base = mapinfo->map_region(ro)) != NULL &&
|
||
- mapinfo->verify_region_checksum(ro) &&
|
||
- (_rw_base = mapinfo->map_region(rw)) != NULL &&
|
||
- mapinfo->verify_region_checksum(rw) &&
|
||
- (_md_base = mapinfo->map_region(md)) != NULL &&
|
||
- mapinfo->verify_region_checksum(md) &&
|
||
- (_mc_base = mapinfo->map_region(mc)) != NULL &&
|
||
- mapinfo->verify_region_checksum(mc) &&
|
||
- (image_alignment == (size_t)max_alignment()) &&
|
||
- mapinfo->validate_classpath_entry_table()) {
|
||
- // Success (no need to do anything)
|
||
- return true;
|
||
+ if (mapinfo->is_static()) {
|
||
+ if ((_ro_base = mapinfo->map_region(ro)) != NULL &&
|
||
+ mapinfo->verify_region_checksum(ro) &&
|
||
+ (_rw_base = mapinfo->map_region(rw)) != NULL &&
|
||
+ mapinfo->verify_region_checksum(rw) &&
|
||
+ (_md_base = mapinfo->map_region(md)) != NULL &&
|
||
+ mapinfo->verify_region_checksum(md) &&
|
||
+ (_mc_base = mapinfo->map_region(mc)) != NULL &&
|
||
+ mapinfo->verify_region_checksum(mc) &&
|
||
+ (image_alignment == (size_t)max_alignment()) &&
|
||
+ mapinfo->validate_classpath_entry_table()) {
|
||
+ mapinfo->set_is_mapped(true);
|
||
+ return true;
|
||
+ }
|
||
} else {
|
||
- // If there was a failure in mapping any of the spaces, unmap the ones
|
||
- // that succeeded
|
||
- if (_ro_base != NULL) mapinfo->unmap_region(ro);
|
||
- if (_rw_base != NULL) mapinfo->unmap_region(rw);
|
||
- if (_md_base != NULL) mapinfo->unmap_region(md);
|
||
- if (_mc_base != NULL) mapinfo->unmap_region(mc);
|
||
+ if ((_rw_base = mapinfo->map_region(d_rw)) != NULL &&
|
||
+ mapinfo->verify_region_checksum(d_rw) &&
|
||
+ (_ro_base = mapinfo->map_region(d_ro)) != NULL &&
|
||
+ mapinfo->verify_region_checksum(d_ro) &&
|
||
+ (image_alignment == (size_t)max_alignment())) {
|
||
+ mapinfo->set_is_mapped(true);
|
||
+ return true;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ // If there was a failure in mapping any of the spaces, unmap the ones
|
||
+ // that succeeded
|
||
+ if (_ro_base != NULL) mapinfo->unmap_region(ro);
|
||
+ if (_rw_base != NULL) mapinfo->unmap_region(rw);
|
||
+ if (_md_base != NULL) mapinfo->unmap_region(md);
|
||
+ if (_mc_base != NULL) mapinfo->unmap_region(mc);
|
||
#ifndef _WINDOWS
|
||
- // Release the entire mapped region
|
||
- shared_rs.release();
|
||
+ // 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.");
|
||
- } else {
|
||
- FLAG_SET_DEFAULT(UseSharedSpaces, false);
|
||
- }
|
||
- return false;
|
||
+ // 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.");
|
||
+ } else {
|
||
+ FLAG_SET_DEFAULT(UseSharedSpaces, false);
|
||
}
|
||
+ return false;
|
||
}
|
||
|
||
+void** MetaspaceShared::_vtbl_list = NULL;
|
||
+
|
||
+intptr_t* MetaspaceShared::get_archived_vtable(MetaspaceObj::Type msotype, address obj) {
|
||
+ Arguments::assert_is_dumping_archive();
|
||
+ switch (msotype) {
|
||
+ case MetaspaceObj::SymbolType:
|
||
+ case MetaspaceObj::TypeArrayU1Type:
|
||
+ case MetaspaceObj::TypeArrayU2Type:
|
||
+ case MetaspaceObj::TypeArrayU4Type:
|
||
+ case MetaspaceObj::TypeArrayU8Type:
|
||
+ case MetaspaceObj::TypeArrayOtherType:
|
||
+ case MetaspaceObj::ConstMethodType:
|
||
+ case MetaspaceObj::ConstantPoolCacheType:
|
||
+ case MetaspaceObj::AnnotationType:
|
||
+ case MetaspaceObj::MethodCountersType:
|
||
+ // These have no vtables.
|
||
+ break;
|
||
+ case MetaspaceObj::MethodDataType:
|
||
+ // We don't archive MethodData <-- should have been removed in removed_unsharable_info
|
||
+ ShouldNotReachHere();
|
||
+ break;
|
||
+ default:
|
||
+ int vtable_offset = MetaspaceShared::vtbl_list_size * sizeof(void*) + sizeof(intptr_t);
|
||
+ char* vtable_start = (char*)_vtbl_list + vtable_offset;
|
||
+ return (intptr_t*)find_matching_vtbl_ptr(_vtbl_list, (void*)vtable_start, obj);
|
||
+ }
|
||
+ return NULL;
|
||
+}
|
||
// Read the miscellaneous data from the shared file, and
|
||
// serialize it out to its various destinations.
|
||
|
||
@@ -996,6 +1063,7 @@ void MetaspaceShared::initialize_shared_spaces() {
|
||
// for Klass objects. They get filled in later.
|
||
|
||
void** vtbl_list = (void**)buffer;
|
||
+ _vtbl_list = vtbl_list;
|
||
buffer += MetaspaceShared::vtbl_list_size * sizeof(void*);
|
||
Universe::init_self_patching_vtbl_list(vtbl_list, vtbl_list_size);
|
||
|
||
@@ -1079,6 +1147,15 @@ void MetaspaceShared::initialize_shared_spaces() {
|
||
// Close the mapinfo file
|
||
mapinfo->close();
|
||
|
||
+ FileMapInfo *dynamic_mapinfo = FileMapInfo::dynamic_info();
|
||
+ if (dynamic_mapinfo != NULL) {
|
||
+ intptr_t* buffer = (intptr_t*)dynamic_mapinfo->serialized_data();
|
||
+ ReadClosure rc(&buffer);
|
||
+ SymbolTable::serialize_shared_table_header(&rc);
|
||
+ SystemDictionaryShared::serialize_dictionary_headers(&rc);
|
||
+ dynamic_mapinfo->close();
|
||
+ }
|
||
+
|
||
if (PrintSharedArchiveAndExit) {
|
||
if (PrintSharedDictionary) {
|
||
tty->print_cr("\nShared classes:\n");
|
||
@@ -1104,6 +1181,11 @@ bool MetaspaceShared::remap_shared_readonly_as_readwrite() {
|
||
if (!mapinfo->remap_shared_readonly_as_readwrite()) {
|
||
return false;
|
||
}
|
||
+
|
||
+ mapinfo = FileMapInfo::dynamic_info();
|
||
+ if (mapinfo != NULL && !mapinfo->remap_shared_readonly_as_readwrite()) {
|
||
+ return false;
|
||
+ }
|
||
_remapped_readwrite = true;
|
||
}
|
||
return true;
|
||
diff --git a/hotspot/src/share/vm/memory/metaspaceShared.hpp b/hotspot/src/share/vm/memory/metaspaceShared.hpp
|
||
index d58ebecb2..a9dadfbb9 100644
|
||
--- a/hotspot/src/share/vm/memory/metaspaceShared.hpp
|
||
+++ b/hotspot/src/share/vm/memory/metaspaceShared.hpp
|
||
@@ -28,6 +28,7 @@
|
||
#include "memory/memRegion.hpp"
|
||
#include "runtime/virtualspace.hpp"
|
||
#include "utilities/exceptions.hpp"
|
||
+#include "utilities/growableArray.hpp"
|
||
#include "utilities/macros.hpp"
|
||
|
||
#define LargeSharedArchiveSize (300*M)
|
||
@@ -44,6 +45,7 @@
|
||
(uintx)(type ## SharedArchiveSize * region ## RegionPercentage) : Shared ## region ## Size
|
||
|
||
class FileMapInfo;
|
||
+class SerializeClosure;
|
||
|
||
// Class Data Sharing Support
|
||
class MetaspaceShared : AllStatic {
|
||
@@ -56,6 +58,11 @@ class MetaspaceShared : AllStatic {
|
||
static bool _has_error_classes;
|
||
static bool _archive_loading_failed;
|
||
static bool _remapped_readwrite;
|
||
+ static void* _shared_metaspace_static_bottom;
|
||
+ static void** _vtbl_list; // Remember the vtable start address for dynamic dump metadata
|
||
+ static char* _requested_base_address;
|
||
+ static void* _shared_metaspace_dynamic_base;
|
||
+ static void* _shared_metaspace_dynamic_top;
|
||
public:
|
||
enum {
|
||
vtbl_list_size = 17, // number of entries in the shared space vtable list.
|
||
@@ -71,11 +78,20 @@ class MetaspaceShared : AllStatic {
|
||
};
|
||
|
||
enum {
|
||
- ro = 0, // read-only shared space in the heap
|
||
- rw = 1, // read-write shared space in the heap
|
||
- md = 2, // miscellaneous data for initializing tables, etc.
|
||
- mc = 3, // miscellaneous code - vtable replacement.
|
||
- n_regions = 4
|
||
+ // core archive spaces
|
||
+ ro = 0, // read-only shared space in the heap
|
||
+ rw = 1, // read-write shared space in the heap
|
||
+ md = 2, // miscellaneous data for initializing tables, etc. (static only)
|
||
+ mc = 3, // miscellaneous code - vtable replacement. (static only)
|
||
+ n_regions = 4 // total number of static regions
|
||
+ };
|
||
+
|
||
+ enum {
|
||
+ // core dynamic archive spaces
|
||
+ d_rw = 0, // read-write shared space in the heap
|
||
+ d_ro = 1, // read-only shared space in the heap
|
||
+ d_bm = 2, // relocation bitmaps (freed after file mapping is finished)
|
||
+ d_n_regions = 2 // d_rw and d_ro
|
||
};
|
||
|
||
// Accessor functions to save shared space created for metadata, which has
|
||
@@ -108,6 +124,28 @@ class MetaspaceShared : AllStatic {
|
||
_archive_loading_failed = true;
|
||
}
|
||
static bool map_shared_spaces(FileMapInfo* mapinfo) NOT_CDS_RETURN_(false);
|
||
+
|
||
+ static bool is_shared_dynamic(const void* p) {
|
||
+ return p < _shared_metaspace_dynamic_top && p >= _shared_metaspace_dynamic_base;
|
||
+ }
|
||
+
|
||
+ // This is the base address as specified by -XX:SharedBaseAddress during -Xshare:dump.
|
||
+ // Both the base/top archives are written using this as their base address.
|
||
+ //
|
||
+ // During static dump: _requested_base_address == SharedBaseAddress.
|
||
+ //
|
||
+ // During dynamic dump: _requested_base_address is not always the same as SharedBaseAddress:
|
||
+ // - SharedBaseAddress is used for *reading the base archive*. I.e., CompactHashtable uses
|
||
+ // it to convert offsets to pointers to Symbols in the base archive.
|
||
+ // The base archive may be mapped to an OS-selected address due to ASLR. E.g.,
|
||
+ // you may have SharedBaseAddress == 0x00ff123400000000.
|
||
+ // - _requested_base_address is used for *writing the output archive*. It's usually
|
||
+ // 0x800000000 (unless it was set by -XX:SharedBaseAddress during -Xshare:dump).
|
||
+ static char* requested_base_address() {
|
||
+ return _requested_base_address;
|
||
+ }
|
||
+
|
||
+ static intptr_t* get_archived_vtable(MetaspaceObj::Type msotype, address obj);
|
||
static void initialize_shared_spaces() NOT_CDS_RETURN;
|
||
|
||
// Return true if given address is in the mapped shared space.
|
||
@@ -138,5 +176,8 @@ class MetaspaceShared : AllStatic {
|
||
|
||
static int count_class(const char* classlist_file);
|
||
static void estimate_regions_size() NOT_CDS_RETURN;
|
||
+
|
||
+ static void set_shared_metaspace_static_bottom(void* bottom) { _shared_metaspace_static_bottom = bottom; }
|
||
+ static void* shared_metaspace_static_bottom() { return _shared_metaspace_static_bottom; }
|
||
};
|
||
#endif // SHARE_VM_MEMORY_METASPACE_SHARED_HPP
|
||
diff --git a/hotspot/src/share/vm/oops/annotations.cpp b/hotspot/src/share/vm/oops/annotations.cpp
|
||
index 776b8606b..6b3080f17 100644
|
||
--- a/hotspot/src/share/vm/oops/annotations.cpp
|
||
+++ b/hotspot/src/share/vm/oops/annotations.cpp
|
||
@@ -27,6 +27,7 @@
|
||
#include "memory/heapInspection.hpp"
|
||
#include "memory/metadataFactory.hpp"
|
||
#include "memory/oopFactory.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
#include "oops/annotations.hpp"
|
||
#include "oops/instanceKlass.hpp"
|
||
#include "utilities/ostream.hpp"
|
||
@@ -36,6 +37,17 @@ Annotations* Annotations::allocate(ClassLoaderData* loader_data, TRAPS) {
|
||
return new (loader_data, size(), true, MetaspaceObj::AnnotationType, THREAD) Annotations();
|
||
}
|
||
|
||
+void Annotations::metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Iter(Annotations): %p", this);
|
||
+ }
|
||
+
|
||
+ it->push(&_class_annotations);
|
||
+ it->push(&_fields_annotations);
|
||
+ it->push(&_class_type_annotations);
|
||
+ it->push(&_fields_type_annotations); // FIXME: need a test case where _fields_type_annotations != NULL
|
||
+}
|
||
+
|
||
// helper
|
||
void Annotations::free_contents(ClassLoaderData* loader_data, Array<AnnotationArray*>* p) {
|
||
if (p != NULL) {
|
||
diff --git a/hotspot/src/share/vm/oops/annotations.hpp b/hotspot/src/share/vm/oops/annotations.hpp
|
||
index ad405a8db..d1f7bc71b 100644
|
||
--- a/hotspot/src/share/vm/oops/annotations.hpp
|
||
+++ b/hotspot/src/share/vm/oops/annotations.hpp
|
||
@@ -35,6 +35,7 @@
|
||
class ClassLoaderData;
|
||
class outputStream;
|
||
class KlassSizeStats;
|
||
+class MetaspaceClosure;
|
||
|
||
typedef Array<u1> AnnotationArray;
|
||
|
||
@@ -54,6 +55,8 @@ class Annotations: public MetaspaceObj {
|
||
Array<AnnotationArray*>* _fields_type_annotations;
|
||
|
||
public:
|
||
+ void metaspace_pointers_do(MetaspaceClosure* it);
|
||
+
|
||
// Allocate instance of this class
|
||
static Annotations* allocate(ClassLoaderData* loader_data, TRAPS);
|
||
|
||
@@ -61,8 +64,14 @@ class Annotations: public MetaspaceObj {
|
||
void deallocate_contents(ClassLoaderData* loader_data);
|
||
DEBUG_ONLY(bool on_stack() { return false; }) // for template
|
||
|
||
+ // Annotations should be stored in the read-only region of CDS archive.
|
||
+ static bool is_read_only_by_default() { return true; }
|
||
+
|
||
+ MetaspaceObj::Type type() const { return AnnotationType; }
|
||
+
|
||
// Sizing (in words)
|
||
static int size() { return sizeof(Annotations) / wordSize; }
|
||
+
|
||
#if INCLUDE_SERVICES
|
||
void collect_statistics(KlassSizeStats *sz) const;
|
||
#endif
|
||
diff --git a/hotspot/src/share/vm/oops/arrayKlass.cpp b/hotspot/src/share/vm/oops/arrayKlass.cpp
|
||
index 129bce63d..9009d6972 100644
|
||
--- a/hotspot/src/share/vm/oops/arrayKlass.cpp
|
||
+++ b/hotspot/src/share/vm/oops/arrayKlass.cpp
|
||
@@ -30,6 +30,7 @@
|
||
#include "jvmtifiles/jvmti.h"
|
||
#include "memory/gcLocker.hpp"
|
||
#include "memory/universe.inline.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
#include "oops/arrayKlass.hpp"
|
||
#include "oops/arrayOop.hpp"
|
||
#include "oops/instanceKlass.hpp"
|
||
@@ -64,6 +65,19 @@ oop ArrayKlass::multi_allocate(int rank, jint* sizes, TRAPS) {
|
||
return NULL;
|
||
}
|
||
|
||
+void ArrayKlass::metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ Klass::metaspace_pointers_do(it);
|
||
+
|
||
+ if (TraceDynamicCDS) {
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("Iter(InstanceKlass): %p (%s)", this, external_name());
|
||
+ }
|
||
+
|
||
+ // need to cast away volatile
|
||
+ it->push((Klass**)&_higher_dimension);
|
||
+ it->push((Klass**)&_lower_dimension);
|
||
+}
|
||
+
|
||
// find field according to JVM spec 5.4.3.2, returns the klass in which the field is defined
|
||
Klass* ArrayKlass::find_field(Symbol* name, Symbol* sig, fieldDescriptor* fd) const {
|
||
// There are no fields in an array klass but look to the super class (Object)
|
||
@@ -203,6 +217,14 @@ void ArrayKlass::remove_unshareable_info() {
|
||
_higher_dimension = NULL;
|
||
}
|
||
|
||
+void ArrayKlass::remove_java_mirror() {
|
||
+ Klass::remove_java_mirror();
|
||
+ if (_higher_dimension != NULL) {
|
||
+ ArrayKlass *ak = ArrayKlass::cast(higher_dimension());
|
||
+ ak->remove_java_mirror();
|
||
+ }
|
||
+}
|
||
+
|
||
void ArrayKlass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) {
|
||
assert(loader_data == ClassLoaderData::the_null_class_loader_data(), "array classes belong to null loader");
|
||
Klass::restore_unshareable_info(loader_data, protection_domain, CHECK);
|
||
diff --git a/hotspot/src/share/vm/oops/arrayKlass.hpp b/hotspot/src/share/vm/oops/arrayKlass.hpp
|
||
index d28ece376..9b6fd9e0b 100644
|
||
--- a/hotspot/src/share/vm/oops/arrayKlass.hpp
|
||
+++ b/hotspot/src/share/vm/oops/arrayKlass.hpp
|
||
@@ -100,7 +100,7 @@ class ArrayKlass: public Klass {
|
||
|
||
GrowableArray<Klass*>* compute_secondary_supers(int num_extra_slots);
|
||
bool compute_is_subtype_of(Klass* k);
|
||
-
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure* it);
|
||
// Sizing
|
||
static int header_size() { return sizeof(ArrayKlass)/HeapWordSize; }
|
||
static int static_size(int header_size);
|
||
@@ -141,6 +141,7 @@ class ArrayKlass: public Klass {
|
||
|
||
// CDS support - remove and restore oops from metadata. Oops are not shared.
|
||
virtual void remove_unshareable_info();
|
||
+ virtual void remove_java_mirror();
|
||
virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS);
|
||
|
||
// Printing
|
||
diff --git a/hotspot/src/share/vm/oops/constMethod.cpp b/hotspot/src/share/vm/oops/constMethod.cpp
|
||
index a496149df..fc7d74512 100644
|
||
--- a/hotspot/src/share/vm/oops/constMethod.cpp
|
||
+++ b/hotspot/src/share/vm/oops/constMethod.cpp
|
||
@@ -26,6 +26,7 @@
|
||
#include "interpreter/interpreter.hpp"
|
||
#include "memory/gcLocker.hpp"
|
||
#include "memory/heapInspection.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
#include "memory/metadataFactory.hpp"
|
||
#include "oops/constMethod.hpp"
|
||
#include "oops/method.hpp"
|
||
@@ -148,6 +149,31 @@ Method* ConstMethod::method() const {
|
||
return _constants->pool_holder()->method_with_idnum(_method_idnum);
|
||
}
|
||
|
||
+void ConstMethod::metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Iter(ConstMethod): %p", this);
|
||
+ }
|
||
+
|
||
+ if (!method()->method_holder()->is_rewritten()) {
|
||
+ it->push(&_constants, MetaspaceClosure::_writable);
|
||
+ } else {
|
||
+ it->push(&_constants);
|
||
+ }
|
||
+ it->push(&_stackmap_data);
|
||
+ if (has_method_annotations()) {
|
||
+ it->push(method_annotations_addr());
|
||
+ }
|
||
+ if (has_parameter_annotations()) {
|
||
+ it->push(parameter_annotations_addr());
|
||
+ }
|
||
+ if (has_type_annotations()) {
|
||
+ it->push(type_annotations_addr());
|
||
+ }
|
||
+ if (has_default_annotations()) {
|
||
+ it->push(default_annotations_addr());
|
||
+ }
|
||
+}
|
||
+
|
||
// linenumber table - note that length is unknown until decompression,
|
||
// see class CompressedLineNumberReadStream.
|
||
|
||
diff --git a/hotspot/src/share/vm/oops/constMethod.hpp b/hotspot/src/share/vm/oops/constMethod.hpp
|
||
index 0caa3a26f..20cff631e 100644
|
||
--- a/hotspot/src/share/vm/oops/constMethod.hpp
|
||
+++ b/hotspot/src/share/vm/oops/constMethod.hpp
|
||
@@ -129,7 +129,7 @@ class MethodParametersElement VALUE_OBJ_CLASS_SPEC {
|
||
};
|
||
|
||
class KlassSizeStats;
|
||
-
|
||
+class MetaspaceClosure;
|
||
// Class to collect the sizes of ConstMethod inline tables
|
||
#define INLINE_TABLES_DO(do_element) \
|
||
do_element(localvariable_table_length) \
|
||
@@ -344,6 +344,12 @@ public:
|
||
// Size needed
|
||
static int size(int code_size, InlineTableSizes* sizes);
|
||
|
||
+ // ConstMethods should be stored in the read-only region of CDS archive.
|
||
+ static bool is_read_only_by_default() { return true; }
|
||
+
|
||
+ void metaspace_pointers_do(MetaspaceClosure* it);
|
||
+ MetaspaceObj::Type type() const { return ConstMethodType; }
|
||
+
|
||
int size() const { return _constMethod_size;}
|
||
void set_constMethod_size(int size) { _constMethod_size = size; }
|
||
#if INCLUDE_SERVICES
|
||
diff --git a/hotspot/src/share/vm/oops/constantPool.cpp b/hotspot/src/share/vm/oops/constantPool.cpp
|
||
index b6158e4e9..f8078bffa 100644
|
||
--- a/hotspot/src/share/vm/oops/constantPool.cpp
|
||
+++ b/hotspot/src/share/vm/oops/constantPool.cpp
|
||
@@ -23,16 +23,19 @@
|
||
*/
|
||
|
||
#include "precompiled.hpp"
|
||
+#include "cds/archiveUtils.hpp"
|
||
#include "classfile/classLoaderData.hpp"
|
||
#include "classfile/javaClasses.hpp"
|
||
#include "classfile/metadataOnStackMark.hpp"
|
||
#include "classfile/symbolTable.hpp"
|
||
#include "classfile/systemDictionary.hpp"
|
||
+#include "classfile/systemDictionaryShared.hpp"
|
||
#include "classfile/vmSymbols.hpp"
|
||
#include "interpreter/linkResolver.hpp"
|
||
#include "memory/heapInspection.hpp"
|
||
#include "memory/metadataFactory.hpp"
|
||
#include "memory/oopFactory.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
#include "oops/constantPool.hpp"
|
||
#include "oops/instanceKlass.hpp"
|
||
#include "oops/objArrayKlass.hpp"
|
||
@@ -153,6 +156,52 @@ void ConstantPool::initialize_resolved_references(ClassLoaderData* loader_data,
|
||
}
|
||
}
|
||
|
||
+void ConstantPool::metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Iter(ConstantPool): %p", this);
|
||
+ }
|
||
+
|
||
+ it->push(&_tags, MetaspaceClosure::_writable);
|
||
+ it->push(&_cache);
|
||
+ it->push(&_pool_holder);
|
||
+ it->push(&_operands);
|
||
+ it->push(&_reference_map, MetaspaceClosure::_writable);
|
||
+
|
||
+ for (int i = 0; i < length(); i++) {
|
||
+ // About resolved klasses, we should be careful because of data structure difference
|
||
+ // between jdk8 and jdk17.
|
||
+ constantTag ctag = tag_at(i);
|
||
+ if (ctag.is_string() || ctag.is_utf8() || ctag.is_replaced_symbol()) {
|
||
+ it->push(symbol_at_addr(i));
|
||
+ } else if (ctag.is_klass()) {
|
||
+ it->push((Klass**)obj_at_addr_raw(i));
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
+// We replace data in base() by normal symbol in two conditions:
|
||
+// 1. resolved klass
|
||
+// The value is klass ptr, in remove_unshareable_info we need replace klass ptr by klass‘s
|
||
+// name. The klass may be excluded, hence klass ptr is NULL and lost klass'name
|
||
+// at the end. Replace excluded klasses by names.
|
||
+// 2. unresolved klass
|
||
+// The value is symbol ptr | 1, the data is unparseable pushed in MetaspaceClosure, we need
|
||
+// replace the data by a normal symbol ptr at first, and store value symbol ptr | 1 at last.
|
||
+void ConstantPool::symbol_replace_excluded_klass() {
|
||
+ for (int i = 0; i < length(); i++) {
|
||
+ constantTag ctag = tag_at(i);
|
||
+ if (ctag.is_klass()) {
|
||
+ Klass* klass = resolved_klass_at(i);
|
||
+ if (SystemDictionaryShared::is_excluded_class((InstanceKlass*)klass)) {
|
||
+ replaced_symbol_at_put(i, klass->name());
|
||
+ }
|
||
+ } else if (ctag.is_unresolved_klass()) {
|
||
+ CPSlot entry = slot_at(i);
|
||
+ replaced_symbol_at_put(i, entry.get_symbol());
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
// CDS support. Create a new resolved_references array.
|
||
void ConstantPool::restore_unshareable_info(TRAPS) {
|
||
|
||
@@ -180,18 +229,30 @@ void ConstantPool::restore_unshareable_info(TRAPS) {
|
||
}
|
||
|
||
void ConstantPool::remove_unshareable_info() {
|
||
- if (UseAppCDS) {
|
||
- if (cache() != NULL) {
|
||
- cache()->reset();
|
||
+ if (cache() != NULL) {
|
||
+ cache()->remove_unshareable_info();
|
||
+ }
|
||
+
|
||
+ // Shared ConstantPools are in the RO region, so the _flags cannot be modified.
|
||
+ // The _on_stack flag is used to prevent ConstantPools from deallocation during
|
||
+ // class redefinition. Since shared ConstantPools cannot be deallocated anyway,
|
||
+ // we always set _on_stack to true to avoid having to change _flags during runtime.
|
||
+ _flags |= _on_stack;
|
||
+ int num_klasses = 0;
|
||
+ for (int index = 1; index < length(); index++) { // Index 0 is unused
|
||
+ if (tag_at(index).is_unresolved_klass_in_error()) {
|
||
+ tag_at_put(index, JVM_CONSTANT_UnresolvedClass);
|
||
+ } else if (tag_at(index).is_method_handle_in_error()) {
|
||
+ tag_at_put(index, JVM_CONSTANT_MethodHandle);
|
||
+ } else if (tag_at(index).is_method_type_in_error()) {
|
||
+ tag_at_put(index, JVM_CONSTANT_MethodType);
|
||
}
|
||
- for (int i = 0; i < _length; i++) {
|
||
- if (tag_at(i).is_klass()) {
|
||
- Klass* resolvedKlass = resolved_klass_at(i);
|
||
- ResourceMark rm;
|
||
- char* name = resolvedKlass->name()->as_C_string();
|
||
- int len = strlen(name);
|
||
- unresolved_klass_at_put(i, resolvedKlass->name());
|
||
- }
|
||
+
|
||
+ if (tag_at(index).is_klass()) {
|
||
+ Klass* resolved_Klass = resolved_klass_at(index);
|
||
+ unresolved_klass_at_put(index, resolved_Klass->name());
|
||
+ } else if (tag_at(index).is_replaced_symbol()) {
|
||
+ unresolved_klass_at_put(index, *symbol_at_addr(index));
|
||
}
|
||
}
|
||
// Resolved references are not in the shared archive.
|
||
@@ -519,8 +580,14 @@ Klass* ConstantPool::klass_ref_at(int which, TRAPS) {
|
||
|
||
|
||
Symbol* ConstantPool::klass_name_at(int which) const {
|
||
- assert(tag_at(which).is_unresolved_klass() || tag_at(which).is_klass(),
|
||
- "Corrupted constant pool");
|
||
+ // Dynamic CDS dump need call here in verify, release version no need do it.
|
||
+#ifndef PRODUCT
|
||
+ assert(tag_at(which).is_unresolved_klass() || tag_at(which).is_klass() ||
|
||
+ tag_at(which).is_replaced_symbol(), "Corrupted constant pool");
|
||
+ if (tag_at(which).is_replaced_symbol()) {
|
||
+ return *symbol_at_addr(which);
|
||
+ }
|
||
+#endif
|
||
// A resolved constantPool entry will contain a Klass*, otherwise a Symbol*.
|
||
// It is not safe to rely on the tag bit's here, since we don't have a lock, and the entry and
|
||
// tag is not updated atomicly.
|
||
diff --git a/hotspot/src/share/vm/oops/constantPool.hpp b/hotspot/src/share/vm/oops/constantPool.hpp
|
||
index ec111df04..b5b4db38b 100644
|
||
--- a/hotspot/src/share/vm/oops/constantPool.hpp
|
||
+++ b/hotspot/src/share/vm/oops/constantPool.hpp
|
||
@@ -231,6 +231,9 @@ class ConstantPool : public Metadata {
|
||
return cache()->entry_at(cp_cache_index);
|
||
}
|
||
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure* it);
|
||
+ void symbol_replace_excluded_klass();
|
||
+ virtual MetaspaceObj::Type type() const { return ConstantPoolType; }
|
||
// Assembly code support
|
||
static int tags_offset_in_bytes() { return offset_of(ConstantPool, _tags); }
|
||
static int cache_offset_in_bytes() { return offset_of(ConstantPool, _cache); }
|
||
@@ -315,6 +318,11 @@ class ConstantPool : public Metadata {
|
||
*symbol_at_addr(which) = s;
|
||
}
|
||
|
||
+ void replaced_symbol_at_put(int which, Symbol*s) {
|
||
+ tag_at_put(which, JVM_CONSTANT_ReplacedSymbol);
|
||
+ *symbol_at_addr(which) = s;
|
||
+ }
|
||
+
|
||
void string_at_put(int which, int obj_index, oop str) {
|
||
resolved_references()->obj_at_put(obj_index, str);
|
||
}
|
||
@@ -747,6 +755,10 @@ class ConstantPool : public Metadata {
|
||
void collect_statistics(KlassSizeStats *sz) const;
|
||
#endif
|
||
|
||
+ // ConstantPools should be stored in the read-only region of CDS archive.
|
||
+ // But the vtable will be patched in JDK8, so it must be writable.
|
||
+ static bool is_read_only_by_default() { return false; }
|
||
+
|
||
friend class ClassFileParser;
|
||
friend class SystemDictionary;
|
||
|
||
diff --git a/hotspot/src/share/vm/oops/cpCache.cpp b/hotspot/src/share/vm/oops/cpCache.cpp
|
||
index ebcf3d6a9..51f5397b8 100644
|
||
--- a/hotspot/src/share/vm/oops/cpCache.cpp
|
||
+++ b/hotspot/src/share/vm/oops/cpCache.cpp
|
||
@@ -24,14 +24,17 @@
|
||
|
||
#include "precompiled.hpp"
|
||
#include "gc_implementation/shared/markSweep.inline.hpp"
|
||
+#include "interpreter/bytecodeStream.hpp"
|
||
#include "interpreter/interpreter.hpp"
|
||
#include "interpreter/rewriter.hpp"
|
||
#include "memory/universe.inline.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
#include "oops/cpCache.hpp"
|
||
#include "oops/objArrayOop.hpp"
|
||
#include "oops/oop.inline.hpp"
|
||
#include "prims/jvmtiRedefineClassesTrace.hpp"
|
||
#include "prims/methodHandles.hpp"
|
||
+#include "runtime/arguments.hpp"
|
||
#include "runtime/handles.inline.hpp"
|
||
#include "runtime/orderAccess.inline.hpp"
|
||
#include "utilities/macros.hpp"
|
||
@@ -602,6 +605,72 @@ void ConstantPoolCache::initialize(const intArray& inverse_index_map,
|
||
}
|
||
}
|
||
|
||
+void ConstantPoolCache::metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Iter(ConstantPoolCache): %p", this);
|
||
+ }
|
||
+ it->push(&_constant_pool);
|
||
+ // it->push(&_reference_map);
|
||
+}
|
||
+
|
||
+void ConstantPoolCache::remove_unshareable_info() {
|
||
+ walk_entries_for_initialization(/*check_only = */ false);
|
||
+}
|
||
+
|
||
+void ConstantPoolCache::walk_entries_for_initialization(bool check_only) {
|
||
+ Arguments::assert_is_dumping_archive();
|
||
+ // When dumping the archive, we want to clean up the ConstantPoolCache
|
||
+ // to remove any effect of linking due to the execution of Java code --
|
||
+ // each ConstantPoolCacheEntry will have the same contents as if
|
||
+ // ConstantPoolCache::initialize has just returned:
|
||
+ //
|
||
+ // - We keep the ConstantPoolCache::constant_pool_index() bits for all entries.
|
||
+ // - We keep the "f2" field for entries used by invokedynamic and invokehandle
|
||
+ // - All other bits in the entries are cleared to zero.
|
||
+ ResourceMark rm;
|
||
+
|
||
+ InstanceKlass* ik = constant_pool()->pool_holder();
|
||
+ bool* f2_used = NEW_RESOURCE_ARRAY(bool, length());
|
||
+ memset(f2_used, 0, sizeof(bool) * length());
|
||
+
|
||
+ Thread* current = Thread::current();
|
||
+
|
||
+ // Find all the slots that we need to preserve f2
|
||
+ for (int i = 0; i < ik->methods()->length(); i++) {
|
||
+ Method* m = ik->methods()->at(i);
|
||
+ RawBytecodeStream bcs(methodHandle(current, m));
|
||
+ while (!bcs.is_last_bytecode()) {
|
||
+ Bytecodes::Code opcode = bcs.raw_next();
|
||
+ switch (opcode) {
|
||
+ case Bytecodes::_invokedynamic: {
|
||
+ int index = Bytes::get_native_u4(bcs.bcp() + 1);
|
||
+ int cp_cache_index = constant_pool()->invokedynamic_cp_cache_index(index);
|
||
+ f2_used[cp_cache_index] = 1;
|
||
+ }
|
||
+ break;
|
||
+ case Bytecodes::_invokehandle: {
|
||
+ int cp_cache_index = Bytes::get_native_u2(bcs.bcp() + 1);
|
||
+ f2_used[cp_cache_index] = 1;
|
||
+ }
|
||
+ break;
|
||
+ default:
|
||
+ break;
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (check_only) {
|
||
+ DEBUG_ONLY(
|
||
+ for (int i=0; i<length(); i++) {
|
||
+ entry_at(i)->verify_just_initialized(f2_used[i]);
|
||
+ })
|
||
+ } else {
|
||
+ for (int i=0; i<length(); i++) {
|
||
+ entry_at(i)->reinitialize(f2_used[i]);
|
||
+ }
|
||
+ }
|
||
+}
|
||
+
|
||
#if INCLUDE_JVMTI
|
||
// RedefineClasses() API support:
|
||
// If any entry of this ConstantPoolCache points to any of
|
||
diff --git a/hotspot/src/share/vm/oops/cpCache.hpp b/hotspot/src/share/vm/oops/cpCache.hpp
|
||
index 48f9bbd27..cb2fa43d6 100644
|
||
--- a/hotspot/src/share/vm/oops/cpCache.hpp
|
||
+++ b/hotspot/src/share/vm/oops/cpCache.hpp
|
||
@@ -124,6 +124,7 @@ class PSPromotionManager;
|
||
// source code. The _indices field with the bytecode must be written last.
|
||
|
||
class CallInfo;
|
||
+class MetaspaceClosure;
|
||
|
||
class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC {
|
||
friend class VMStructs;
|
||
@@ -397,6 +398,24 @@ class ConstantPoolCacheEntry VALUE_OBJ_CLASS_SPEC {
|
||
// When shifting flags as a 32-bit int, make sure we don't need an extra mask for tos_state:
|
||
assert((((u4)-1 >> tos_state_shift) & ~tos_state_mask) == 0, "no need for tos_state mask");
|
||
}
|
||
+
|
||
+ void reinitialize(bool f2_used) {
|
||
+ _indices &= cp_index_mask;
|
||
+ _f1 = NULL;
|
||
+ _flags = 0;
|
||
+ if (!f2_used) {
|
||
+ _f2 = 0;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ void verify_just_initialized(bool f2_used) {
|
||
+ assert((_indices & (~cp_index_mask)) == 0, "sanity");
|
||
+ assert(_f1 == NULL, "sanity");
|
||
+ assert(_flags == 0, "sanity");
|
||
+ if (!f2_used) {
|
||
+ assert(_f2 == 0, "sanity");
|
||
+ }
|
||
+}
|
||
};
|
||
|
||
|
||
@@ -468,6 +487,10 @@ class ConstantPoolCache: public MetaspaceObj {
|
||
return base() + i;
|
||
}
|
||
|
||
+ void metaspace_pointers_do(MetaspaceClosure* it);
|
||
+ void remove_unshareable_info();
|
||
+ void walk_entries_for_initialization(bool check_only);
|
||
+ MetaspaceObj::Type type() const { return ConstantPoolCacheType; }
|
||
// Code generation
|
||
static ByteSize base_offset() { return in_ByteSize(sizeof(ConstantPoolCache)); }
|
||
static ByteSize entry_offset(int raw_index) {
|
||
@@ -488,7 +511,7 @@ class ConstantPoolCache: public MetaspaceObj {
|
||
#endif // INCLUDE_JVMTI
|
||
|
||
void reset();
|
||
-
|
||
+
|
||
// Deallocate - no fields to deallocate
|
||
DEBUG_ONLY(bool on_stack() { return false; })
|
||
void deallocate_contents(ClassLoaderData* data) {}
|
||
diff --git a/hotspot/src/share/vm/oops/instanceKlass.cpp b/hotspot/src/share/vm/oops/instanceKlass.cpp
|
||
index 367c9a09d..0d1b1a8d0 100644
|
||
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp
|
||
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp
|
||
@@ -39,6 +39,7 @@
|
||
#include "memory/iterator.inline.hpp"
|
||
#include "memory/metadataFactory.hpp"
|
||
#include "memory/oopFactory.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
#include "oops/fieldStreams.hpp"
|
||
#include "oops/instanceClassLoaderKlass.hpp"
|
||
#include "oops/instanceKlass.hpp"
|
||
@@ -53,6 +54,7 @@
|
||
#include "prims/jvmtiRedefineClasses.hpp"
|
||
#include "prims/jvmtiThreadState.hpp"
|
||
#include "prims/methodComparator.hpp"
|
||
+#include "runtime/arguments.hpp"
|
||
#include "runtime/fieldDescriptor.hpp"
|
||
#include "runtime/handles.inline.hpp"
|
||
#include "runtime/javaCalls.hpp"
|
||
@@ -463,12 +465,73 @@ void InstanceKlass::deallocate_contents(ClassLoaderData* loader_data) {
|
||
MetadataFactory::free_metadata(loader_data, annotations());
|
||
}
|
||
set_annotations(NULL);
|
||
+
|
||
+ if (Arguments::is_dumping_archive()) {
|
||
+ SystemDictionaryShared::remove_dumptime_info(this);
|
||
+ }
|
||
}
|
||
|
||
bool InstanceKlass::should_be_initialized() const {
|
||
return !is_initialized();
|
||
}
|
||
|
||
+void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ Klass::metaspace_pointers_do(it);
|
||
+
|
||
+ if (TraceDynamicCDS) {
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("Iter(InstanceKlass): %p (%s)", this, external_name());
|
||
+ }
|
||
+
|
||
+ it->push(&_annotations);
|
||
+ it->push((Klass**)&_array_klasses);
|
||
+ if (!is_rewritten()) {
|
||
+ it->push(&_constants, MetaspaceClosure::_writable);
|
||
+ } else {
|
||
+ it->push(&_constants);
|
||
+ }
|
||
+ it->push(&_inner_classes);
|
||
+#if INCLUDE_JVMTI
|
||
+ it->push(&_previous_versions);
|
||
+#endif
|
||
+ it->push(&_array_name);
|
||
+ it->push(&_methods);
|
||
+ it->push(&_default_methods);
|
||
+ it->push(&_local_interfaces);
|
||
+ it->push(&_transitive_interfaces);
|
||
+ it->push(&_method_ordering);
|
||
+ if (!is_rewritten()) {
|
||
+ it->push(&_default_vtable_indices, MetaspaceClosure::_writable);
|
||
+ } else {
|
||
+ it->push(&_default_vtable_indices);
|
||
+ }
|
||
+
|
||
+ // _fields might be written into by Rewriter::scan_method() -> fd.set_has_initialized_final_update()
|
||
+ it->push(&_fields, MetaspaceClosure::_writable);
|
||
+
|
||
+ if (itable_length() > 0) {
|
||
+ itableOffsetEntry* ioe = (itableOffsetEntry*)start_of_itable();
|
||
+ int method_table_offset_in_words = ioe->offset()/wordSize;
|
||
+ int nof_interfaces = (method_table_offset_in_words - itable_offset_in_words())
|
||
+ / itableOffsetEntry::size();
|
||
+
|
||
+ for (int i = 0; i < nof_interfaces; i ++, ioe ++) {
|
||
+ if (ioe->interface_klass() != NULL) {
|
||
+ it->push(ioe->interface_klass_addr());
|
||
+ itableMethodEntry* ime = ioe->first_method_entry(this);
|
||
+ int n = klassItable::method_count_for_interface(ioe->interface_klass());
|
||
+ for (int index = 0; index < n; index ++) {
|
||
+ it->push(ime[index].method_addr());
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ // it->push(&_nest_members);
|
||
+ // it->push(&_permitted_subclasses);
|
||
+ // it->push(&_record_components);
|
||
+}
|
||
+
|
||
klassVtable* InstanceKlass::vtable() const {
|
||
return new klassVtable(this, start_of_vtable(), vtable_length() / vtableEntry::size());
|
||
}
|
||
@@ -765,6 +828,28 @@ bool InstanceKlass::link_class_impl(
|
||
}
|
||
|
||
|
||
+// Check if a class or any of its supertypes has a version older than 50.
|
||
+// CDS will not perform verification of old classes during dump time because
|
||
+// without changing the old verifier, the verification constraint cannot be
|
||
+// retrieved during dump time.
|
||
+// Verification of archived old classes will be performed during run time.
|
||
+bool InstanceKlass::can_be_verified_at_dumptime() const {
|
||
+ if (major_version() < 50 /*JAVA_6_VERSION*/) {
|
||
+ return false;
|
||
+ }
|
||
+ if (java_super() != NULL && !java_super()->can_be_verified_at_dumptime()) {
|
||
+ return false;
|
||
+ }
|
||
+ Array<Klass*>* interfaces = local_interfaces();
|
||
+ int len = interfaces->length();
|
||
+ for (int i = 0; i < len; i++) {
|
||
+ if (!((InstanceKlass*)interfaces->at(i))->can_be_verified_at_dumptime()) {
|
||
+ return false;
|
||
+ }
|
||
+ }
|
||
+ return true;
|
||
+}
|
||
+
|
||
// Rewrite the byte codes of all of the methods of a class.
|
||
// The rewriter must be called exactly once. Rewriting must happen after
|
||
// verification but before the first method of the class is executed.
|
||
@@ -1459,7 +1544,32 @@ static int linear_search(Array<Method*>* methods, Symbol* name, Symbol* signatur
|
||
}
|
||
#endif
|
||
|
||
+bool InstanceKlass::_disable_method_binary_search = false;
|
||
+
|
||
+NOINLINE int linear_search(const Array<Method*>* methods, const Symbol* name) {
|
||
+ int len = methods->length();
|
||
+ int l = 0;
|
||
+ int h = len - 1;
|
||
+ while (l <= h) {
|
||
+ Method* m = methods->at(l);
|
||
+ if (m->name() == name) {
|
||
+ return l;
|
||
+ }
|
||
+ l++;
|
||
+ }
|
||
+ return -1;
|
||
+}
|
||
+
|
||
static int binary_search(Array<Method*>* methods, Symbol* name) {
|
||
+ if (InstanceKlass::_disable_method_binary_search) {
|
||
+ assert(DynamicDumpSharedSpaces, "must be");
|
||
+ // 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.
|
||
+ return linear_search(methods, name);
|
||
+ }
|
||
+
|
||
int len = methods->length();
|
||
// methods are sorted, so do binary search
|
||
int l = 0;
|
||
@@ -2455,24 +2565,37 @@ void InstanceKlass::remove_unshareable_info() {
|
||
m->remove_unshareable_info();
|
||
}
|
||
|
||
- if (UseAppCDS) {
|
||
+ if (UseAppCDS || DynamicDumpSharedSpaces) {
|
||
if (_oop_map_cache != NULL) {
|
||
delete _oop_map_cache;
|
||
_oop_map_cache = NULL;
|
||
}
|
||
-
|
||
+
|
||
JNIid::deallocate(jni_ids());
|
||
set_jni_ids(NULL);
|
||
-
|
||
+
|
||
jmethodID* jmeths = methods_jmethod_ids_acquire();
|
||
if (jmeths != (jmethodID*)NULL) {
|
||
release_set_methods_jmethod_ids(NULL);
|
||
FreeHeap(jmeths);
|
||
}
|
||
}
|
||
-
|
||
// do array classes also.
|
||
array_klasses_do(remove_unshareable_in_class);
|
||
+ // These are not allocated from metaspace. They are safe to set to NULL.
|
||
+ _member_names = NULL;
|
||
+ _dependencies = NULL;
|
||
+ _osr_nmethods_head = NULL;
|
||
+ _init_thread = NULL;
|
||
+}
|
||
+
|
||
+void InstanceKlass::remove_java_mirror() {
|
||
+ Klass::remove_java_mirror();
|
||
+
|
||
+ // do array classes also.
|
||
+ if (array_klasses() != NULL) {
|
||
+ array_klasses()->remove_java_mirror();
|
||
+ }
|
||
}
|
||
|
||
static void restore_unshareable_in_class(Klass* k, TRAPS) {
|
||
diff --git a/hotspot/src/share/vm/oops/instanceKlass.hpp b/hotspot/src/share/vm/oops/instanceKlass.hpp
|
||
index 39d2c580c..43919e83d 100644
|
||
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp
|
||
+++ b/hotspot/src/share/vm/oops/instanceKlass.hpp
|
||
@@ -323,6 +323,7 @@ class InstanceKlass: public Klass {
|
||
friend class SystemDictionary;
|
||
|
||
public:
|
||
+ static bool _disable_method_binary_search;
|
||
bool has_nonstatic_fields() const {
|
||
return (_misc_flags & _misc_has_nonstatic_fields) != 0;
|
||
}
|
||
@@ -488,6 +489,7 @@ class InstanceKlass: public Klass {
|
||
void link_class(TRAPS);
|
||
bool link_class_or_fail(TRAPS); // returns false on failure
|
||
void unlink_class();
|
||
+ bool can_be_verified_at_dumptime() const;
|
||
void rewrite_class(TRAPS);
|
||
void link_methods(TRAPS);
|
||
Method* class_initializer();
|
||
@@ -525,6 +527,10 @@ class InstanceKlass: public Klass {
|
||
Method* find_method(Symbol* name, Symbol* signature) const;
|
||
static Method* find_method(Array<Method*>* methods, Symbol* name, Symbol* signature);
|
||
|
||
+ static void disable_method_binary_search() {
|
||
+ _disable_method_binary_search = true;
|
||
+ }
|
||
+
|
||
// find a local method, but skip static methods
|
||
Method* find_instance_method(Symbol* name, Symbol* signature,
|
||
PrivateLookupMode private_mode);
|
||
@@ -1001,7 +1007,8 @@ class InstanceKlass: public Klass {
|
||
bool can_be_fastpath_allocated() const {
|
||
return !layout_helper_needs_slow_path(layout_helper());
|
||
}
|
||
-
|
||
+
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure* iter);
|
||
// Java vtable/itable
|
||
klassVtable* vtable() const; // return new klassVtable wrapper
|
||
inline Method* method_at_vtable(int index);
|
||
@@ -1075,7 +1082,7 @@ class InstanceKlass: public Klass {
|
||
|
||
public:
|
||
void set_in_error_state() {
|
||
- assert(DumpSharedSpaces, "only call this when dumping archive");
|
||
+ assert(DumpSharedSpaces || DynamicDumpSharedSpaces, "only call this when dumping archive");
|
||
_init_state = initialization_error;
|
||
}
|
||
bool check_sharing_error_state();
|
||
@@ -1150,6 +1157,7 @@ private:
|
||
public:
|
||
// CDS support - remove and restore oops from metadata. Oops are not shared.
|
||
virtual void remove_unshareable_info();
|
||
+ virtual void remove_java_mirror();
|
||
virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS);
|
||
|
||
// jvm support
|
||
diff --git a/hotspot/src/share/vm/oops/klass.cpp b/hotspot/src/share/vm/oops/klass.cpp
|
||
index 5269060a4..34d9d9895 100644
|
||
--- a/hotspot/src/share/vm/oops/klass.cpp
|
||
+++ b/hotspot/src/share/vm/oops/klass.cpp
|
||
@@ -26,16 +26,19 @@
|
||
#include "classfile/javaClasses.hpp"
|
||
#include "classfile/dictionary.hpp"
|
||
#include "classfile/systemDictionary.hpp"
|
||
+#include "classfile/systemDictionaryShared.hpp"
|
||
#include "classfile/vmSymbols.hpp"
|
||
#include "gc_implementation/shared/markSweep.inline.hpp"
|
||
#include "gc_interface/collectedHeap.inline.hpp"
|
||
#include "memory/heapInspection.hpp"
|
||
#include "memory/metadataFactory.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
#include "memory/oopFactory.hpp"
|
||
#include "memory/resourceArea.hpp"
|
||
#include "oops/instanceKlass.hpp"
|
||
#include "oops/klass.inline.hpp"
|
||
#include "oops/oop.inline2.hpp"
|
||
+#include "runtime/arguments.hpp"
|
||
#include "runtime/atomic.inline.hpp"
|
||
#include "runtime/orderAccess.inline.hpp"
|
||
#include "utilities/stack.hpp"
|
||
@@ -69,6 +72,10 @@ ClassLoaderData *Klass::_fake_loader_data_Ext = reinterpret_cast<ClassLoaderData
|
||
void Klass::set_name(Symbol* n) {
|
||
_name = n;
|
||
if (_name != NULL) _name->increment_refcount();
|
||
+
|
||
+ if (Arguments::is_dumping_archive() && oop_is_instance()) {
|
||
+ SystemDictionaryShared::init_dumptime_info(InstanceKlass::cast(this));
|
||
+ }
|
||
}
|
||
|
||
bool Klass::is_subclass_of(const Klass* k) const {
|
||
@@ -369,6 +376,36 @@ GrowableArray<Klass*>* Klass::compute_secondary_supers(int num_extra_slots) {
|
||
return NULL;
|
||
}
|
||
|
||
+void Klass::metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ if (TraceDynamicCDS) {
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("Iter(Klass): %p (%s)", this, external_name());
|
||
+ }
|
||
+
|
||
+ it->push(&_name);
|
||
+ it->push(&_secondary_super_cache);
|
||
+ it->push(&_secondary_supers);
|
||
+ for (int i = 0; i < _primary_super_limit; i++) {
|
||
+ it->push(&_primary_supers[i]);
|
||
+ }
|
||
+ it->push(&_super);
|
||
+ it->push((Klass**)&_subklass);
|
||
+ it->push((Klass**)&_next_sibling);
|
||
+ it->push(&_next_link);
|
||
+
|
||
+ vtableEntry* vt = start_of_vtable();
|
||
+ for (int i = 0; i < vtable_length(); i++) {
|
||
+ it->push(vt[i].method_addr());
|
||
+ }
|
||
+}
|
||
+
|
||
+inline vtableEntry* Klass::start_of_vtable() const {
|
||
+ return (vtableEntry*) ((address)this + in_bytes(vtable_start_offset()));
|
||
+}
|
||
+
|
||
+inline ByteSize Klass::vtable_start_offset() {
|
||
+ return in_ByteSize(InstanceKlass::header_size() * wordSize);
|
||
+}
|
||
|
||
Klass* Klass::subklass() const {
|
||
return _subklass == NULL ? NULL : _subklass;
|
||
@@ -530,7 +567,7 @@ void Klass::oops_do(OopClosure* cl) {
|
||
}
|
||
|
||
void Klass::remove_unshareable_info() {
|
||
- assert (DumpSharedSpaces, "only called for DumpSharedSpaces");
|
||
+ assert (DumpSharedSpaces || DynamicDumpSharedSpaces, "only called for DumpSharedSpaces or DynamicDumpSharedSpaces");
|
||
|
||
JFR_ONLY(REMOVE_ID(this);)
|
||
set_subklass(NULL);
|
||
@@ -539,40 +576,46 @@ void Klass::remove_unshareable_info() {
|
||
set_java_mirror(NULL);
|
||
set_next_link(NULL);
|
||
|
||
- if (!UseAppCDS) {
|
||
- // CDS logic
|
||
+ if (class_loader_data() == NULL) {
|
||
+ // Null out class loader data for classes loaded by bootstrap (null) loader
|
||
+ set_class_loader_data(NULL);
|
||
+ } else if (SystemDictionary::is_ext_class_loader(class_loader())) {
|
||
+ // Mark class loaded by system class loader
|
||
+ set_class_loader_data(_fake_loader_data_Ext);
|
||
+ } else if (SystemDictionary::is_app_class_loader(class_loader())) {
|
||
+ set_class_loader_data(_fake_loader_data_App);
|
||
+ } else {
|
||
+ // Class loader data for classes loaded by customer loader
|
||
set_class_loader_data(NULL);
|
||
- } else if (class_loader_data() != NULL) {
|
||
- // AppCDS logic
|
||
- if (class_loader() == NULL) {
|
||
- // Null out class loader data for classes loaded by bootstrap (null) loader
|
||
- set_class_loader_data(NULL);
|
||
- } else if(SystemDictionary::is_ext_class_loader(class_loader())) {
|
||
- // Mark class loaded by system class loader
|
||
- set_class_loader_data(_fake_loader_data_Ext);
|
||
- } else {
|
||
- set_class_loader_data(_fake_loader_data_App);
|
||
- }
|
||
}
|
||
}
|
||
|
||
+void Klass::remove_java_mirror() {
|
||
+ Arguments::assert_is_dumping_archive();
|
||
+ if (TraceDynamicCDS) {
|
||
+ ResourceMark rm;
|
||
+ dynamic_cds_log->print_cr("remove java_mirror: %s", external_name());
|
||
+ }
|
||
+ // Just null out the mirror. The class_loader_data() no longer exists.
|
||
+ _java_mirror = NULL;
|
||
+}
|
||
+
|
||
void Klass::restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS) {
|
||
JFR_ONLY(RESTORE_ID(this);)
|
||
+ if (TraceDynamicCDS) {
|
||
+ ResourceMark rm(THREAD);
|
||
+ dynamic_cds_log->print_cr("restore: %s", external_name());
|
||
+ }
|
||
+
|
||
// If an exception happened during CDS restore, some of these fields may already be
|
||
// set. We leave the class on the CLD list, even if incomplete so that we don't
|
||
// modify the CLD list outside a safepoint.
|
||
if (class_loader_data() == NULL || has_fake_loader_data()) {
|
||
- // CDS should not set fake loader data
|
||
- assert(!has_fake_loader_data() || (has_fake_loader_data() && UseAppCDS),
|
||
- "setting fake loader data possible only with AppCDS enabled");
|
||
- // Restore class_loader_data
|
||
set_class_loader_data(loader_data);
|
||
-
|
||
// Add to class loader list first before creating the mirror
|
||
// (same order as class file parsing)
|
||
loader_data->add_class(this);
|
||
}
|
||
-
|
||
// Recreate the class mirror.
|
||
// Only recreate it if not present. A previous attempt to restore may have
|
||
// gotten an OOM later but keep the mirror if it was created.
|
||
diff --git a/hotspot/src/share/vm/oops/klass.hpp b/hotspot/src/share/vm/oops/klass.hpp
|
||
index f70587eab..4e45a7756 100644
|
||
--- a/hotspot/src/share/vm/oops/klass.hpp
|
||
+++ b/hotspot/src/share/vm/oops/klass.hpp
|
||
@@ -94,6 +94,8 @@ class ParCompactionManager;
|
||
class KlassSizeStats;
|
||
class fieldDescriptor;
|
||
class MarkSweep;
|
||
+class MetaspaceClosure;
|
||
+class vtableEntry;
|
||
|
||
class Klass : public Metadata {
|
||
friend class VMStructs;
|
||
@@ -209,7 +211,7 @@ protected:
|
||
bool has_fake_loader_data_App() { return class_loader_data() == _fake_loader_data_App; }
|
||
bool has_fake_loader_data_Ext() { return class_loader_data() == _fake_loader_data_Ext; }
|
||
bool has_fake_loader_data() { return (has_fake_loader_data_App() || has_fake_loader_data_Ext()); }
|
||
-
|
||
+
|
||
bool is_klass() const volatile { return true; }
|
||
|
||
// super
|
||
@@ -316,6 +318,7 @@ protected:
|
||
_shared_class_path_index = index;
|
||
};
|
||
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure* it);
|
||
|
||
protected: // internal accessors
|
||
Klass* subklass_oop() const { return _subklass; }
|
||
@@ -323,7 +326,10 @@ protected:
|
||
void set_subklass(Klass* s);
|
||
void set_next_sibling(Klass* s);
|
||
|
||
+ vtableEntry* start_of_vtable() const;
|
||
+
|
||
public:
|
||
+ static ByteSize vtable_start_offset();
|
||
|
||
// Compiler support
|
||
static ByteSize super_offset() { return in_ByteSize(offset_of(Klass, _super)); }
|
||
@@ -505,6 +511,7 @@ protected:
|
||
public:
|
||
// CDS support - remove and restore oops from metadata. Oops are not shared.
|
||
virtual void remove_unshareable_info();
|
||
+ virtual void remove_java_mirror();
|
||
virtual void restore_unshareable_info(ClassLoaderData* loader_data, Handle protection_domain, TRAPS);
|
||
|
||
protected:
|
||
@@ -725,6 +732,7 @@ protected:
|
||
|
||
virtual const char* internal_name() const = 0;
|
||
|
||
+ virtual MetaspaceObj::Type type() const { return ClassType; }
|
||
// Verification
|
||
virtual void verify_on(outputStream* st);
|
||
void verify() { verify_on(tty); }
|
||
diff --git a/hotspot/src/share/vm/oops/klassVtable.hpp b/hotspot/src/share/vm/oops/klassVtable.hpp
|
||
index 244f3c0cc..9379bcca0 100644
|
||
--- a/hotspot/src/share/vm/oops/klassVtable.hpp
|
||
+++ b/hotspot/src/share/vm/oops/klassVtable.hpp
|
||
@@ -176,6 +176,7 @@ class vtableEntry VALUE_OBJ_CLASS_SPEC {
|
||
}
|
||
static int method_offset_in_bytes() { return offset_of(vtableEntry, _method); }
|
||
Method* method() const { return _method; }
|
||
+ Method** method_addr() { return &_method; }
|
||
|
||
private:
|
||
Method* _method;
|
||
@@ -216,6 +217,7 @@ class itableOffsetEntry VALUE_OBJ_CLASS_SPEC {
|
||
int _offset;
|
||
public:
|
||
Klass* interface_klass() const { return _interface; }
|
||
+ InstanceKlass**interface_klass_addr() { return(InstanceKlass**) &_interface; }
|
||
int offset() const { return _offset; }
|
||
|
||
static itableMethodEntry* method_entry(Klass* k, int offset) { return (itableMethodEntry*)(((address)k) + offset); }
|
||
@@ -238,6 +240,7 @@ class itableMethodEntry VALUE_OBJ_CLASS_SPEC {
|
||
|
||
public:
|
||
Method* method() const { return _method; }
|
||
+ Method**method_addr() { return &_method; }
|
||
|
||
void clear() { _method = NULL; }
|
||
|
||
diff --git a/hotspot/src/share/vm/oops/metadata.hpp b/hotspot/src/share/vm/oops/metadata.hpp
|
||
index dc52c452e..372faa953 100644
|
||
--- a/hotspot/src/share/vm/oops/metadata.hpp
|
||
+++ b/hotspot/src/share/vm/oops/metadata.hpp
|
||
@@ -28,6 +28,7 @@
|
||
#include "utilities/exceptions.hpp"
|
||
#include "utilities/globalDefinitions.hpp"
|
||
#include "utilities/ostream.hpp"
|
||
+class MetaspaceClosure;
|
||
|
||
// This is the base class for an internal Class related metadata
|
||
class Metadata : public MetaspaceObj {
|
||
@@ -47,8 +48,9 @@ class Metadata : public MetaspaceObj {
|
||
virtual bool is_method() const volatile { return false; }
|
||
virtual bool is_methodData() const volatile { return false; }
|
||
virtual bool is_constantPool() const volatile { return false; }
|
||
-
|
||
+ virtual MetaspaceObj::Type type() const = 0;
|
||
virtual const char* internal_name() const = 0;
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure* iter) {}
|
||
|
||
void print() const { print_on(tty); }
|
||
void print_value() const { print_value_on(tty); }
|
||
diff --git a/hotspot/src/share/vm/oops/method.cpp b/hotspot/src/share/vm/oops/method.cpp
|
||
index 64cdae9c7..305348bd0 100644
|
||
--- a/hotspot/src/share/vm/oops/method.cpp
|
||
+++ b/hotspot/src/share/vm/oops/method.cpp
|
||
@@ -37,6 +37,7 @@
|
||
#include "memory/heapInspection.hpp"
|
||
#include "memory/metadataFactory.hpp"
|
||
#include "memory/metaspaceShared.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
#include "memory/oopFactory.hpp"
|
||
#include "oops/constMethod.hpp"
|
||
#include "oops/methodData.hpp"
|
||
@@ -834,6 +835,20 @@ void Method::set_not_osr_compilable(int comp_level, bool report, const char* rea
|
||
assert(!CompilationPolicy::can_be_osr_compiled(this, comp_level), "sanity check");
|
||
}
|
||
|
||
+void Method::metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Iter(Method): %p", this);
|
||
+ }
|
||
+
|
||
+ if (!method_holder()->is_rewritten()) {
|
||
+ it->push(&_constMethod, MetaspaceClosure::_writable);
|
||
+ } else {
|
||
+ it->push(&_constMethod);
|
||
+ }
|
||
+ it->push(&_method_data);
|
||
+ it->push(&_method_counters);
|
||
+}
|
||
+
|
||
// Revert to using the interpreter and clear out the nmethod
|
||
void Method::clear_code(bool acquire_lock /* = true */) {
|
||
MutexLockerEx pl(acquire_lock ? Patching_lock : NULL, Mutex::_no_safepoint_check_flag);
|
||
@@ -1421,12 +1436,15 @@ static int method_comparator(Method* a, Method* b) {
|
||
|
||
// 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 idempotent, bool set_idnums) {
|
||
+void Method::sort_methods(Array<Method*>* methods, bool idempotent, bool set_idnums, method_comparator_func func) {
|
||
int length = methods->length();
|
||
if (length > 1) {
|
||
+ if (func == NULL) {
|
||
+ func = method_comparator;
|
||
+ }
|
||
{
|
||
No_Safepoint_Verifier nsv;
|
||
- QuickSort::sort<Method*>(methods->data(), length, method_comparator, idempotent);
|
||
+ QuickSort::sort<Method*>(methods->data(), length, func, idempotent);
|
||
}
|
||
// Reset method ordering
|
||
if (set_idnums) {
|
||
diff --git a/hotspot/src/share/vm/oops/method.hpp b/hotspot/src/share/vm/oops/method.hpp
|
||
index 1f507ac0f..ec93f2fb4 100644
|
||
--- a/hotspot/src/share/vm/oops/method.hpp
|
||
+++ b/hotspot/src/share/vm/oops/method.hpp
|
||
@@ -99,6 +99,7 @@ class MethodCounters;
|
||
class ConstMethod;
|
||
class InlineTableSizes;
|
||
class KlassSizeStats;
|
||
+class MetaspaceClosure;
|
||
|
||
class Method : public Metadata {
|
||
friend class VMStructs;
|
||
@@ -857,6 +858,9 @@ class Method : public Metadata {
|
||
void print_made_not_compilable(int comp_level, bool is_osr, bool report, const char* reason);
|
||
|
||
public:
|
||
+ void metaspace_pointers_do(MetaspaceClosure* it);
|
||
+ virtual MetaspaceObj::Type type() const { return MethodType; }
|
||
+
|
||
MethodCounters* get_method_counters(TRAPS) {
|
||
if (_method_counters == NULL) {
|
||
build_method_counters(this, CHECK_AND_CLEAR_NULL);
|
||
@@ -897,8 +901,9 @@ class Method : public Metadata {
|
||
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 idempotent = false, bool set_idnums = true);
|
||
+ static void sort_methods(Array<Method*>* methods, bool idempotent = false, bool set_idnums = true, method_comparator_func func = NULL);
|
||
|
||
// Deallocation function for redefine classes or if an error occurs
|
||
void deallocate_contents(ClassLoaderData* loader_data);
|
||
diff --git a/hotspot/src/share/vm/oops/methodCounters.hpp b/hotspot/src/share/vm/oops/methodCounters.hpp
|
||
index b98644574..6a3f7a738 100644
|
||
--- a/hotspot/src/share/vm/oops/methodCounters.hpp
|
||
+++ b/hotspot/src/share/vm/oops/methodCounters.hpp
|
||
@@ -129,5 +129,12 @@ class MethodCounters: public MetaspaceObj {
|
||
return offset_of(MethodCounters, _interpreter_invocation_count);
|
||
}
|
||
|
||
+ MetaspaceObj::Type type() const { return MethodCountersType; }
|
||
+
|
||
+ void metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Iter(MethodCounters): %p", this);
|
||
+ }
|
||
+ }
|
||
};
|
||
#endif //SHARE_VM_OOPS_METHODCOUNTERS_HPP
|
||
diff --git a/hotspot/src/share/vm/oops/methodData.cpp b/hotspot/src/share/vm/oops/methodData.cpp
|
||
index eb48188a6..bde6ca123 100644
|
||
--- a/hotspot/src/share/vm/oops/methodData.cpp
|
||
+++ b/hotspot/src/share/vm/oops/methodData.cpp
|
||
@@ -29,6 +29,7 @@
|
||
#include "interpreter/bytecodeStream.hpp"
|
||
#include "interpreter/linkResolver.hpp"
|
||
#include "memory/heapInspection.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
#include "oops/methodData.hpp"
|
||
#include "prims/jvmtiRedefineClasses.hpp"
|
||
#include "runtime/compilationPolicy.hpp"
|
||
@@ -1683,3 +1684,11 @@ void MethodData::clean_weak_method_links() {
|
||
clean_extra_data(&cl);
|
||
verify_extra_data_clean(&cl);
|
||
}
|
||
+
|
||
+
|
||
+void MethodData::metaspace_pointers_do(MetaspaceClosure* iter) {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Iter(MethodData): %p", this);
|
||
+ }
|
||
+ iter->push(&_method);
|
||
+}
|
||
diff --git a/hotspot/src/share/vm/oops/methodData.hpp b/hotspot/src/share/vm/oops/methodData.hpp
|
||
index 3cd7cd6f1..eb121268f 100644
|
||
--- a/hotspot/src/share/vm/oops/methodData.hpp
|
||
+++ b/hotspot/src/share/vm/oops/methodData.hpp
|
||
@@ -67,7 +67,7 @@ class KlassSizeStats;
|
||
|
||
// forward decl
|
||
class ProfileData;
|
||
-
|
||
+class MetaspaceClosure;
|
||
// DataLayout
|
||
//
|
||
// Overlay for generic profiling data.
|
||
@@ -2486,6 +2486,9 @@ public:
|
||
void clean_method_data(BoolObjectClosure* is_alive);
|
||
|
||
void clean_weak_method_links();
|
||
+
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure* iter);
|
||
+ virtual MetaspaceObj::Type type() const { return MethodDataType; }
|
||
};
|
||
|
||
#endif // SHARE_VM_OOPS_METHODDATAOOP_HPP
|
||
diff --git a/hotspot/src/share/vm/oops/objArrayKlass.cpp b/hotspot/src/share/vm/oops/objArrayKlass.cpp
|
||
index 19abfbd5a..60d173e9e 100644
|
||
--- a/hotspot/src/share/vm/oops/objArrayKlass.cpp
|
||
+++ b/hotspot/src/share/vm/oops/objArrayKlass.cpp
|
||
@@ -33,6 +33,7 @@
|
||
#include "memory/metadataFactory.hpp"
|
||
#include "memory/resourceArea.hpp"
|
||
#include "memory/universe.inline.hpp"
|
||
+#include "memory/metaspaceClosure.hpp"
|
||
#include "oops/instanceKlass.hpp"
|
||
#include "oops/klass.inline.hpp"
|
||
#include "oops/objArrayKlass.hpp"
|
||
@@ -569,6 +570,12 @@ int ObjArrayKlass::oop_adjust_pointers(oop obj) {
|
||
return size;
|
||
}
|
||
|
||
+void ObjArrayKlass::metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ ArrayKlass::metaspace_pointers_do(it);
|
||
+ it->push(&_element_klass);
|
||
+ it->push(&_bottom_klass);
|
||
+}
|
||
+
|
||
#if INCLUDE_ALL_GCS
|
||
void ObjArrayKlass::oop_push_contents(PSPromotionManager* pm, oop obj) {
|
||
assert(obj->is_objArray(), "obj must be obj array");
|
||
diff --git a/hotspot/src/share/vm/oops/objArrayKlass.hpp b/hotspot/src/share/vm/oops/objArrayKlass.hpp
|
||
index ab3cbc61c..c17adba70 100644
|
||
--- a/hotspot/src/share/vm/oops/objArrayKlass.hpp
|
||
+++ b/hotspot/src/share/vm/oops/objArrayKlass.hpp
|
||
@@ -109,7 +109,8 @@ class ObjArrayKlass : public ArrayKlass {
|
||
template <class T> inline void objarray_follow_contents(oop obj, int index, MarkSweep* mark);
|
||
|
||
int oop_adjust_pointers(oop obj);
|
||
-
|
||
+
|
||
+ virtual void metaspace_pointers_do(MetaspaceClosure* iter);
|
||
// Parallel Scavenge and Parallel Old
|
||
PARALLEL_GC_DECLS
|
||
#if INCLUDE_ALL_GCS
|
||
diff --git a/hotspot/src/share/vm/oops/symbol.hpp b/hotspot/src/share/vm/oops/symbol.hpp
|
||
index aaa55c589..4b1b5cb5d 100644
|
||
--- a/hotspot/src/share/vm/oops/symbol.hpp
|
||
+++ b/hotspot/src/share/vm/oops/symbol.hpp
|
||
@@ -25,9 +25,9 @@
|
||
#ifndef SHARE_VM_OOPS_SYMBOL_HPP
|
||
#define SHARE_VM_OOPS_SYMBOL_HPP
|
||
|
||
-#include "utilities/utf8.hpp"
|
||
#include "memory/allocation.hpp"
|
||
#include "runtime/atomic.hpp"
|
||
+#include "utilities/utf8.hpp"
|
||
|
||
// A Symbol is a canonicalized string.
|
||
// All Symbols reside in global SymbolTable and are reference counted.
|
||
@@ -101,6 +101,7 @@
|
||
// Since sometimes this is allocated from Metadata, pick a base allocation
|
||
// type without virtual functions.
|
||
class ClassLoaderData;
|
||
+class MetaspaceClosure;
|
||
|
||
// We separate the fields in SymbolBase from Symbol::_body so that
|
||
// Symbol::size(int) can correctly calculate the space needed.
|
||
@@ -113,7 +114,7 @@ class SymbolBase : public MetaspaceObj {
|
||
int _identity_hash;
|
||
};
|
||
|
||
-class Symbol : private SymbolBase {
|
||
+class Symbol : public SymbolBase {
|
||
friend class VMStructs;
|
||
friend class SymbolTable;
|
||
friend class MoveSymbols;
|
||
@@ -160,6 +161,9 @@ class Symbol : private SymbolBase {
|
||
int refcount() const { return _refcount; }
|
||
void increment_refcount();
|
||
void decrement_refcount();
|
||
+ bool is_permanent() const {
|
||
+ return (refcount() == -1);
|
||
+ }
|
||
|
||
int byte_at(int index) const {
|
||
assert(index >=0 && index < _length, "symbol index overflow");
|
||
@@ -180,6 +184,17 @@ class Symbol : private SymbolBase {
|
||
return starts_with(prefix, (int) strlen(prefix));
|
||
}
|
||
|
||
+ void set_permanent() {
|
||
+ _refcount = -1;
|
||
+ }
|
||
+
|
||
+ void metaspace_pointers_do(MetaspaceClosure* it) {
|
||
+ if (TraceDynamicCDS) {
|
||
+ dynamic_cds_log->print_cr("Iter(Symbol): %p", this);
|
||
+ }
|
||
+ }
|
||
+
|
||
+ MetaspaceObj::Type type() const { return SymbolType; }
|
||
// Tests if the symbol starts with the given prefix.
|
||
int index_of_at(int i, const char* str, int len) const;
|
||
int index_of_at(int i, const char* str) const {
|
||
@@ -208,6 +223,9 @@ class Symbol : private SymbolBase {
|
||
|
||
jchar* as_unicode(int& length) const;
|
||
|
||
+ // Symbols should be stored in the read-only region of CDS archive.
|
||
+ static bool is_read_only_by_default() { return true; }
|
||
+
|
||
// Treating this symbol as a class name, returns the Java name for the class.
|
||
// String is allocated in resource area if buffer is not provided.
|
||
// See Klass::external_name()
|
||
diff --git a/hotspot/src/share/vm/runtime/arguments.cpp b/hotspot/src/share/vm/runtime/arguments.cpp
|
||
index 6f5e75107..1f603021a 100644
|
||
--- a/hotspot/src/share/vm/runtime/arguments.cpp
|
||
+++ b/hotspot/src/share/vm/runtime/arguments.cpp
|
||
@@ -29,6 +29,7 @@
|
||
#include "compiler/compilerOracle.hpp"
|
||
#include "memory/allocation.inline.hpp"
|
||
#include "memory/cardTableRS.hpp"
|
||
+#include "memory/filemap.hpp"
|
||
#include "memory/genCollectedHeap.hpp"
|
||
#include "memory/referenceProcessor.hpp"
|
||
#include "memory/universe.inline.hpp"
|
||
@@ -126,6 +127,7 @@ bool Arguments::_BackgroundCompilation = BackgroundCompilation;
|
||
bool Arguments::_ClipInlining = ClipInlining;
|
||
|
||
char* Arguments::SharedArchivePath = NULL;
|
||
+char* Arguments::SharedDynamicArchivePath = NULL;
|
||
|
||
AgentLibraryList Arguments::_libraryList;
|
||
AgentLibraryList Arguments::_agentList;
|
||
@@ -179,6 +181,117 @@ static void logOption(const char* opt) {
|
||
}
|
||
}
|
||
|
||
+#if INCLUDE_CDS
|
||
+// Sharing support
|
||
+// Construct the path to the archive
|
||
+int Arguments::num_archives(const char* archive_path) {
|
||
+ if (archive_path == NULL) {
|
||
+ return 0;
|
||
+ }
|
||
+ int npaths = 1;
|
||
+ char* p = (char*)archive_path;
|
||
+ while (*p != '\0') {
|
||
+ if (*p == os::path_separator()[0]) {
|
||
+ npaths++;
|
||
+ }
|
||
+ p++;
|
||
+ }
|
||
+ return npaths;
|
||
+}
|
||
+
|
||
+void Arguments::extract_shared_archive_paths(const char* archive_path,
|
||
+ char** base_archive_path,
|
||
+ char** top_archive_path) {
|
||
+ char* begin_ptr = (char*)archive_path;
|
||
+ char* end_ptr = strchr((char*)archive_path, os::path_separator()[0]);
|
||
+ if (end_ptr == NULL || end_ptr == begin_ptr) {
|
||
+ vm_exit_during_initialization("Base archive was not specified", archive_path);
|
||
+ }
|
||
+ size_t len = end_ptr - begin_ptr;
|
||
+ char* cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
|
||
+ strncpy(cur_path, begin_ptr, len);
|
||
+ cur_path[len] = '\0';
|
||
+ FileMapInfo::check_archive((const char*)cur_path, true /*is_static*/);
|
||
+ *base_archive_path = cur_path;
|
||
+
|
||
+ begin_ptr = ++end_ptr;
|
||
+ if (*begin_ptr == '\0') {
|
||
+ vm_exit_during_initialization("Top archive was not specified", archive_path);
|
||
+ }
|
||
+ end_ptr = strchr(begin_ptr, '\0');
|
||
+ assert(end_ptr != NULL, "sanity");
|
||
+ len = end_ptr - begin_ptr;
|
||
+ cur_path = NEW_C_HEAP_ARRAY(char, len + 1, mtInternal);
|
||
+ strncpy(cur_path, begin_ptr, len + 1);
|
||
+
|
||
+ FileMapInfo::check_archive((const char*)cur_path, false /*is_static*/);
|
||
+ *top_archive_path = cur_path;
|
||
+}
|
||
+
|
||
+bool Arguments::init_shared_archive_paths() {
|
||
+ if (ArchiveClassesAtExit != NULL) {
|
||
+ if (DumpSharedSpaces) {
|
||
+ vm_exit_during_initialization("-XX:ArchiveClassesAtExit cannot be used with -Xshare:dump");
|
||
+ }
|
||
+ SharedDynamicArchivePath = os::strdup_check_oom(ArchiveClassesAtExit, mtClassShared);
|
||
+ } else {
|
||
+ if (SharedDynamicArchivePath != NULL) {
|
||
+ os::free(SharedDynamicArchivePath);
|
||
+ SharedDynamicArchivePath = NULL;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (SharedArchiveFile != NULL) {
|
||
+ int archives = num_archives(SharedArchiveFile);
|
||
+ if (is_dumping_archive()) {
|
||
+ if (archives > 1) {
|
||
+ vm_exit_during_initialization(
|
||
+ "Cannot have more than 1 archive file specified in -XX:SharedArchiveFile during CDS dumping");
|
||
+ }
|
||
+ if (DynamicDumpSharedSpaces) {
|
||
+ if (strcmp(SharedArchiveFile, ArchiveClassesAtExit) == 0) {
|
||
+ vm_exit_during_initialization(
|
||
+ "Cannot have the same archive file specified for -XX:SharedArchiveFile and -XX:ArchiveClassesAtExit",
|
||
+ SharedArchiveFile);
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+
|
||
+ if (!is_dumping_archive()) {
|
||
+ if (archives > 2) {
|
||
+ vm_exit_during_initialization(
|
||
+ "Cannot have more than 2 archive files specified in the -XX:SharedArchiveFile option");
|
||
+ }
|
||
+ if (archives == 1) {
|
||
+ char* temp_archive_path = os::strdup_check_oom(SharedArchiveFile, mtClassShared);
|
||
+ int name_size;
|
||
+ bool success =
|
||
+ FileMapInfo::get_base_archive_name_from_header(temp_archive_path, &name_size, &SharedArchivePath);
|
||
+ if (!success) {
|
||
+ SharedArchivePath = temp_archive_path;
|
||
+ } else {
|
||
+ SharedDynamicArchivePath = temp_archive_path;
|
||
+ }
|
||
+ } else {
|
||
+ extract_shared_archive_paths((const char*)SharedArchiveFile,
|
||
+ &SharedArchivePath, &SharedDynamicArchivePath);
|
||
+ }
|
||
+
|
||
+ // We must use tty here instead of dynamic_cds_log for dynamic_cds_log is initialized after share path init.
|
||
+ if (InfoDynamicCDS && SharedArchivePath != NULL) {
|
||
+ tty->print_cr("SharedArchivePath: %s", SharedArchivePath);
|
||
+ }
|
||
+ if (InfoDynamicCDS && SharedDynamicArchivePath != NULL) {
|
||
+ tty->print_cr("SharedDynamicArchivePath: %s", SharedDynamicArchivePath);
|
||
+ }
|
||
+ } else { // CDS dumping
|
||
+ SharedArchivePath = os::strdup_check_oom(SharedArchiveFile, mtClassShared);
|
||
+ }
|
||
+ }
|
||
+ return (SharedArchivePath != NULL);
|
||
+}
|
||
+#endif // INCLUDE_CDS
|
||
+
|
||
// Process java launcher properties.
|
||
void Arguments::process_sun_java_launcher_properties(JavaVMInitArgs* args) {
|
||
// See if sun.java.launcher or sun.java.launcher.pid is defined.
|
||
@@ -3724,6 +3837,30 @@ jint Arguments::finalize_vm_init_args(SysClassPath* scp_p, bool scp_assembly_req
|
||
set_mode_flags(_int);
|
||
}
|
||
|
||
+#if INCLUDE_CDS
|
||
+ if (ArchiveClassesAtExit == NULL) {
|
||
+ FLAG_SET_DEFAULT(DynamicDumpSharedSpaces, false);
|
||
+ } else {
|
||
+ FLAG_SET_DEFAULT(DynamicDumpSharedSpaces, true);
|
||
+ // When Dynamic CDS dump is turned on, we will set ClassUnloading false,
|
||
+ // and there is no need to care if the class loader is alive.
|
||
+ FLAG_SET_DEFAULT(ClassUnloading, false);
|
||
+ }
|
||
+
|
||
+ if (TraceDynamicCDS) {
|
||
+ FLAG_SET_DEFAULT(DebugDynamicCDS, true);
|
||
+ FLAG_SET_DEFAULT(InfoDynamicCDS, true);
|
||
+ } else if (DebugDynamicCDS) {
|
||
+ FLAG_SET_DEFAULT(InfoDynamicCDS, true);
|
||
+ }
|
||
+
|
||
+#ifdef _LP64
|
||
+ // We attempt to set SharedBaseAddress right above
|
||
+ // the java heap base on ObjectAlignmentInBytes.
|
||
+ FLAG_SET_DEFAULT(SharedBaseAddress, (ObjectAlignmentInBytes * 4 * G));
|
||
+#endif // _LP64
|
||
+#endif // INCLUDE_CDS
|
||
+
|
||
// eventually fix up InitialTenuringThreshold if only MaxTenuringThreshold is set
|
||
if (FLAG_IS_DEFAULT(InitialTenuringThreshold) && (InitialTenuringThreshold > MaxTenuringThreshold)) {
|
||
FLAG_SET_ERGO(uintx, InitialTenuringThreshold, MaxTenuringThreshold);
|
||
@@ -3885,6 +4022,11 @@ void Arguments::set_shared_spaces_flags() {
|
||
}
|
||
#endif
|
||
}
|
||
+
|
||
+#if INCLUDE_CDS
|
||
+ // Initialize shared archive paths which could include both base and dynamic archive paths
|
||
+ init_shared_archive_paths();
|
||
+#endif // INCLUDE_CDS
|
||
}
|
||
|
||
#if !INCLUDE_ALL_GCS
|
||
diff --git a/hotspot/src/share/vm/runtime/arguments.hpp b/hotspot/src/share/vm/runtime/arguments.hpp
|
||
index a1fcfc398..19f5cb60b 100644
|
||
--- a/hotspot/src/share/vm/runtime/arguments.hpp
|
||
+++ b/hotspot/src/share/vm/runtime/arguments.hpp
|
||
@@ -443,7 +443,8 @@ class Arguments : AllStatic {
|
||
static bool CheckCompileOnly;
|
||
|
||
static char* SharedArchivePath;
|
||
- static char* AppCDSLockPath;
|
||
+
|
||
+ static char* SharedDynamicArchivePath;
|
||
|
||
public:
|
||
// Parses the arguments, first phase
|
||
@@ -553,6 +554,22 @@ class Arguments : AllStatic {
|
||
|
||
static const char* GetSharedArchivePath() { return SharedArchivePath; }
|
||
|
||
+ static const char* GetSharedDynamicArchivePath() { return SharedDynamicArchivePath; }
|
||
+
|
||
+ static bool init_shared_archive_paths();
|
||
+
|
||
+ static void extract_shared_archive_paths(const char* archive_path,
|
||
+ char** base_archive_path,
|
||
+ char** top_archive_path);
|
||
+
|
||
+ static int num_archives(const char* archive_path);
|
||
+
|
||
+ static bool is_dumping_archive() { return DumpSharedSpaces || DynamicDumpSharedSpaces; }
|
||
+
|
||
+ static void assert_is_dumping_archive() {
|
||
+ assert(Arguments::is_dumping_archive(), "dump time only");
|
||
+ }
|
||
+
|
||
static bool CompileMethod(char* className, char* methodName) {
|
||
return
|
||
methodExists(
|
||
diff --git a/hotspot/src/share/vm/runtime/globals.hpp b/hotspot/src/share/vm/runtime/globals.hpp
|
||
index 91e52f033..eb13ee0d7 100644
|
||
--- a/hotspot/src/share/vm/runtime/globals.hpp
|
||
+++ b/hotspot/src/share/vm/runtime/globals.hpp
|
||
@@ -3910,6 +3910,24 @@ class CommandLineFlags {
|
||
NOT_LP64(LINUX_ONLY(2*G) NOT_LINUX(0)), \
|
||
"Address to allocate shared memory region for class data") \
|
||
\
|
||
+ experimental(ccstr, ArchiveClassesAtExit, NULL, \
|
||
+ "The path and name of the dynamic archive file") \
|
||
+ \
|
||
+ product(bool, InfoDynamicCDS, false, \
|
||
+ "Log info level in DynamicCDS") \
|
||
+ \
|
||
+ product(bool, TraceDynamicCDS, false, \
|
||
+ "Trace details in DynamicCDS") \
|
||
+ \
|
||
+ product(bool, DebugDynamicCDS, false, \
|
||
+ "Debug details in DynamicCDS") \
|
||
+ \
|
||
+ product(bool, DynamicDumpSharedSpaces, false, \
|
||
+ "Dynamic archive") \
|
||
+ \
|
||
+ product(uintx, SharedSymbolTableBucketSize, 4, \
|
||
+ "Average number of symbols per bucket in shared table") \
|
||
+ \
|
||
diagnostic(bool, EnableInvokeDynamic, true, \
|
||
"support JSR 292 (method handles, invokedynamic, " \
|
||
"anonymous classes") \
|
||
@@ -4017,6 +4035,9 @@ class CommandLineFlags {
|
||
"Dump the names all loaded classes, that could be stored into " \
|
||
"the CDS archive, in the specified file") \
|
||
\
|
||
+ product(ccstr, DynamicCDSLog, NULL, \
|
||
+ "Dynamic CDS log path") \
|
||
+ \
|
||
product(ccstr, SharedClassListFile, NULL, \
|
||
"Override the default CDS class list") \
|
||
\
|
||
diff --git a/hotspot/src/share/vm/runtime/java.cpp b/hotspot/src/share/vm/runtime/java.cpp
|
||
index 0a263b017..4f290c826 100644
|
||
--- a/hotspot/src/share/vm/runtime/java.cpp
|
||
+++ b/hotspot/src/share/vm/runtime/java.cpp
|
||
@@ -23,6 +23,7 @@
|
||
*/
|
||
|
||
#include "precompiled.hpp"
|
||
+#include "cds/dynamicArchive.hpp"
|
||
#include "classfile/classLoader.hpp"
|
||
#include "classfile/symbolTable.hpp"
|
||
#include "classfile/systemDictionary.hpp"
|
||
@@ -546,6 +547,13 @@ void before_exit(JavaThread * thread) {
|
||
// Note: we don't wait until it actually dies.
|
||
os::terminate_signal_thread();
|
||
|
||
+#if INCLUDE_CDS
|
||
+ if (DynamicDumpSharedSpaces) {
|
||
+ DynamicArchive::dump();
|
||
+ ShouldNotReachHere();
|
||
+ }
|
||
+#endif
|
||
+
|
||
print_statistics();
|
||
Universe::heap()->print_tracing_info();
|
||
|
||
diff --git a/hotspot/src/share/vm/runtime/mutexLocker.cpp b/hotspot/src/share/vm/runtime/mutexLocker.cpp
|
||
index a96ae50eb..a1c61f864 100644
|
||
--- a/hotspot/src/share/vm/runtime/mutexLocker.cpp
|
||
+++ b/hotspot/src/share/vm/runtime/mutexLocker.cpp
|
||
@@ -39,6 +39,7 @@
|
||
|
||
Mutex* Patching_lock = NULL;
|
||
Monitor* SystemDictionary_lock = NULL;
|
||
+Mutex* SharedDictionary_lock = NULL;
|
||
Mutex* PackageTable_lock = NULL;
|
||
Mutex* CompiledIC_lock = NULL;
|
||
Mutex* InlineCacheBuffer_lock = NULL;
|
||
@@ -129,6 +130,7 @@ Monitor* RedefineClasses_lock = NULL;
|
||
|
||
Mutex* FreeHumongousRegions_lock = NULL;
|
||
|
||
+Mutex* DumpTimeTable_lock = NULL;
|
||
#ifdef INCLUDE_JFR
|
||
Mutex* JfrStacktrace_lock = NULL;
|
||
Monitor* JfrMsg_lock = NULL;
|
||
@@ -224,6 +226,7 @@ void mutex_init() {
|
||
def(JmethodIdCreation_lock , Mutex , leaf, true ); // used for creating jmethodIDs.
|
||
|
||
def(SystemDictionary_lock , Monitor, leaf, true ); // lookups done by VM thread
|
||
+ def(SharedDictionary_lock , Mutex , leaf, true );
|
||
def(PackageTable_lock , Mutex , leaf, false);
|
||
def(InlineCacheBuffer_lock , Mutex , leaf, true );
|
||
def(VMStatistic_lock , Mutex , leaf, false);
|
||
@@ -289,7 +292,7 @@ void mutex_init() {
|
||
def(RedefineClasses_lock , Monitor, nonleaf+5, true);
|
||
|
||
def(FreeHumongousRegions_lock , Mutex , nonleaf, false);
|
||
-
|
||
+ def(DumpTimeTable_lock , Mutex , leaf - 1, true);
|
||
#if INCLUDE_JFR
|
||
def(JfrMsg_lock , Monitor, leaf, true);
|
||
def(JfrBuffer_lock , Mutex, leaf, true);
|
||
diff --git a/hotspot/src/share/vm/runtime/mutexLocker.hpp b/hotspot/src/share/vm/runtime/mutexLocker.hpp
|
||
index 428c80181..f28058b0e 100644
|
||
--- a/hotspot/src/share/vm/runtime/mutexLocker.hpp
|
||
+++ b/hotspot/src/share/vm/runtime/mutexLocker.hpp
|
||
@@ -47,6 +47,7 @@
|
||
|
||
extern Mutex* Patching_lock; // a lock used to guard code patching of compiled code
|
||
extern Monitor* SystemDictionary_lock; // a lock on the system dictonary
|
||
+extern Mutex* SharedDictionary_lock; // a lock on the CDS shared dictionary
|
||
extern Mutex* PackageTable_lock; // a lock on the class loader package table
|
||
extern Mutex* CompiledIC_lock; // a lock used to guard compiled IC patching and access
|
||
extern Mutex* InlineCacheBuffer_lock; // a lock used to guard the InlineCacheBuffer
|
||
@@ -145,6 +146,8 @@ extern Monitor* RedefineClasses_lock; // locks classes from parallel
|
||
|
||
extern Mutex* FreeHumongousRegions_lock; // locks humongous regions from freeing in parallel
|
||
|
||
+extern Mutex* DumpTimeTable_lock; // SystemDictionaryShared::find_or_allocate_info_for
|
||
+
|
||
#if INCLUDE_JFR
|
||
extern Mutex* JfrStacktrace_lock; // used to guard access to the JFR stacktrace table
|
||
extern Monitor* JfrMsg_lock; // protects JFR messaging
|
||
diff --git a/hotspot/src/share/vm/runtime/os.cpp b/hotspot/src/share/vm/runtime/os.cpp
|
||
index ed41265cc..5c5d60220 100644
|
||
--- a/hotspot/src/share/vm/runtime/os.cpp
|
||
+++ b/hotspot/src/share/vm/runtime/os.cpp
|
||
@@ -568,7 +568,7 @@ bool os::find_builtin_agent(AgentLibrary *agent_lib, const char *syms[],
|
||
|
||
// --------------------- heap allocation utilities ---------------------
|
||
|
||
-char *os::strdup(const char *str, MEMFLAGS flags) {
|
||
+char* os::strdup(const char *str, MEMFLAGS flags) {
|
||
size_t size = strlen(str);
|
||
char *dup_str = (char *)malloc(size + 1, flags);
|
||
if (dup_str == NULL) return NULL;
|
||
@@ -576,6 +576,13 @@ char *os::strdup(const char *str, MEMFLAGS flags) {
|
||
return dup_str;
|
||
}
|
||
|
||
+char* os::strdup_check_oom(const char* str, MEMFLAGS flags) {
|
||
+ char* p = os::strdup(str, flags);
|
||
+ if (p == NULL) {
|
||
+ vm_exit_out_of_memory(strlen(str) + 1, OOM_MALLOC_ERROR, "os::strdup_check_oom");
|
||
+ }
|
||
+ return p;
|
||
+}
|
||
|
||
|
||
#define paranoid 0 /* only set to 1 if you suspect checking code has bug */
|
||
diff --git a/hotspot/src/share/vm/runtime/os.hpp b/hotspot/src/share/vm/runtime/os.hpp
|
||
index 296380f39..7ae49fd5b 100644
|
||
--- a/hotspot/src/share/vm/runtime/os.hpp
|
||
+++ b/hotspot/src/share/vm/runtime/os.hpp
|
||
@@ -731,6 +731,8 @@ class os: AllStatic {
|
||
static void free (void *memblock, MEMFLAGS flags = mtNone);
|
||
static bool check_heap(bool force = false); // verify C heap integrity
|
||
static char* strdup(const char *, MEMFLAGS flags = mtInternal); // Like strdup
|
||
+ // Like strdup, but exit VM when strdup() returns NULL
|
||
+ static char* strdup_check_oom(const char*, MEMFLAGS flags = mtInternal);
|
||
|
||
#ifndef PRODUCT
|
||
static julong num_mallocs; // # of calls to malloc/realloc
|
||
diff --git a/hotspot/src/share/vm/runtime/thread.cpp b/hotspot/src/share/vm/runtime/thread.cpp
|
||
index 94b9e69d2..807786d98 100644
|
||
--- a/hotspot/src/share/vm/runtime/thread.cpp
|
||
+++ b/hotspot/src/share/vm/runtime/thread.cpp
|
||
@@ -23,6 +23,7 @@
|
||
*/
|
||
|
||
#include "precompiled.hpp"
|
||
+#include "cds/dynamicArchive.hpp"
|
||
#include "classfile/classLoader.hpp"
|
||
#include "classfile/javaClasses.hpp"
|
||
#include "classfile/systemDictionary.hpp"
|
||
@@ -3934,6 +3935,15 @@ void JavaThread::invoke_shutdown_hooks() {
|
||
this->clear_pending_exception();
|
||
}
|
||
|
||
+#if INCLUDE_CDS
|
||
+ // Link all classes for dynamic CDS dumping before vm exit.
|
||
+ // Same operation is being done in JVM_BeforeHalt for handling the
|
||
+ // case where the application calls System.exit().
|
||
+ if (DynamicDumpSharedSpaces) {
|
||
+ DynamicArchive::prepare_for_dynamic_dumping_at_exit();
|
||
+ }
|
||
+#endif
|
||
+
|
||
EXCEPTION_MARK;
|
||
Klass* k =
|
||
SystemDictionary::resolve_or_null(vmSymbols::java_lang_Shutdown(),
|
||
diff --git a/hotspot/src/share/vm/services/diagnosticCommand.cpp b/hotspot/src/share/vm/services/diagnosticCommand.cpp
|
||
index ede8db156..358ec6e09 100644
|
||
--- a/hotspot/src/share/vm/services/diagnosticCommand.cpp
|
||
+++ b/hotspot/src/share/vm/services/diagnosticCommand.cpp
|
||
@@ -23,6 +23,7 @@
|
||
*/
|
||
|
||
#include "precompiled.hpp"
|
||
+#include "cds/dynamicArchive.hpp"
|
||
#include "classfile/classLoaderStats.hpp"
|
||
#include "gc_implementation/shared/vmGCOperations.hpp"
|
||
#include "runtime/javaCalls.hpp"
|
||
@@ -57,6 +58,7 @@ void DCmdRegistrant::register_dcmds(){
|
||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<FinalizerInfoDCmd>(full_export, true, false));
|
||
#if INCLUDE_SERVICES // Heap dumping/inspection supported
|
||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<HeapDumpDCmd>(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false));
|
||
+ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<DynamicCDSDumpDCmd>(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false));
|
||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassHistogramDCmd>(full_export, true, false));
|
||
DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl<ClassStatsDCmd>(full_export, true, false));
|
||
#endif // INCLUDE_SERVICES
|
||
@@ -375,6 +377,17 @@ int HeapDumpDCmd::num_arguments() {
|
||
}
|
||
}
|
||
|
||
+void DynamicCDSDumpDCmd::execute(DCmdSource source, TRAPS) {
|
||
+#if INCLUDE_CDS
|
||
+ if (DynamicDumpSharedSpaces) {
|
||
+ DynamicArchive::dump();
|
||
+ ShouldNotReachHere();
|
||
+ } else {
|
||
+ warning("Dynamic CDS is not enabled");
|
||
+ }
|
||
+#endif
|
||
+}
|
||
+
|
||
ClassHistogramDCmd::ClassHistogramDCmd(outputStream* output, bool heap) :
|
||
DCmdWithParser(output, heap),
|
||
_all("-all", "Inspect all objects, including unreachable objects",
|
||
diff --git a/hotspot/src/share/vm/services/diagnosticCommand.hpp b/hotspot/src/share/vm/services/diagnosticCommand.hpp
|
||
index b1fb57e53..e28011f25 100644
|
||
--- a/hotspot/src/share/vm/services/diagnosticCommand.hpp
|
||
+++ b/hotspot/src/share/vm/services/diagnosticCommand.hpp
|
||
@@ -267,6 +267,29 @@ public:
|
||
};
|
||
#endif // INCLUDE_SERVICES
|
||
|
||
+class DynamicCDSDumpDCmd : public DCmdWithParser {
|
||
+public:
|
||
+ DynamicCDSDumpDCmd(outputStream* output, bool heap) : DCmdWithParser(output, heap) { }
|
||
+ static const char* name() {
|
||
+ return "GC.dynamic_cds_dump";
|
||
+ }
|
||
+ static const char* description() {
|
||
+ return "Dynamic CDS dump";
|
||
+ }
|
||
+ static const char* impact() {
|
||
+ return "Medium";
|
||
+ }
|
||
+ static const JavaPermission permission() {
|
||
+ JavaPermission p = {"java.lang.management.ManagementPermission",
|
||
+ "monitor", NULL};
|
||
+ return p;
|
||
+ }
|
||
+ static int num_arguments() {
|
||
+ return 0;
|
||
+ }
|
||
+ virtual void execute(DCmdSource source, TRAPS);
|
||
+};
|
||
+
|
||
// See also: inspectheap in attachListener.cpp
|
||
class ClassHistogramDCmd : public DCmdWithParser {
|
||
protected:
|
||
diff --git a/hotspot/src/share/vm/utilities/array.hpp b/hotspot/src/share/vm/utilities/array.hpp
|
||
index 920b87501..371876b56 100644
|
||
--- a/hotspot/src/share/vm/utilities/array.hpp
|
||
+++ b/hotspot/src/share/vm/utilities/array.hpp
|
||
@@ -302,6 +302,7 @@ define_array(intArray , int ) define_stack(intStack , intArray )
|
||
|
||
template <typename T>
|
||
class Array: public MetaspaceObj {
|
||
+ friend class ArchiveBuilder;
|
||
friend class MetadataFactory;
|
||
friend class VMStructs;
|
||
friend class MethodHandleCompiler; // special case
|
||
diff --git a/hotspot/src/share/vm/utilities/bitMap.cpp b/hotspot/src/share/vm/utilities/bitMap.cpp
|
||
index e64add155..12b4b4160 100644
|
||
--- a/hotspot/src/share/vm/utilities/bitMap.cpp
|
||
+++ b/hotspot/src/share/vm/utilities/bitMap.cpp
|
||
@@ -67,16 +67,14 @@ void BitMap::resize(idx_t size_in_bits, bool in_resource_area) {
|
||
idx_t new_size_in_words = size_in_words();
|
||
if (in_resource_area) {
|
||
_map = NEW_RESOURCE_ARRAY(bm_word_t, new_size_in_words);
|
||
+ Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) _map,
|
||
+ MIN2(old_size_in_words, new_size_in_words));
|
||
} else {
|
||
- if (old_map != NULL) {
|
||
- _map_allocator.free();
|
||
- }
|
||
- _map = _map_allocator.allocate(new_size_in_words);
|
||
+ _map = _map_allocator.reallocate(new_size_in_words);
|
||
}
|
||
- Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) _map,
|
||
- MIN2(old_size_in_words, new_size_in_words));
|
||
+
|
||
if (new_size_in_words > old_size_in_words) {
|
||
- clear_range_of_words(old_size_in_words, size_in_words());
|
||
+ clear_range_of_words(old_size_in_words, new_size_in_words);
|
||
}
|
||
}
|
||
|
||
@@ -454,6 +452,11 @@ bool BitMap::is_empty() const {
|
||
return rest == 0 || (*word & right_n_bits((int)rest)) == (bm_word_t) NoBits;
|
||
}
|
||
|
||
+void BitMap::write_to(bm_word_t* buffer, size_t buffer_size_in_bytes) const {
|
||
+ assert(buffer_size_in_bytes == (size_in_words() * BytesPerWord), "must be");
|
||
+ memcpy(buffer, _map, size_in_words() * BytesPerWord);
|
||
+}
|
||
+
|
||
void BitMap::clear_large() {
|
||
clear_large_range_of_words(0, size_in_words());
|
||
}
|
||
diff --git a/hotspot/src/share/vm/utilities/bitMap.hpp b/hotspot/src/share/vm/utilities/bitMap.hpp
|
||
index 51c58da8e..08452bd90 100644
|
||
--- a/hotspot/src/share/vm/utilities/bitMap.hpp
|
||
+++ b/hotspot/src/share/vm/utilities/bitMap.hpp
|
||
@@ -269,6 +269,7 @@ class BitMap VALUE_OBJ_CLASS_SPEC {
|
||
bool is_full() const;
|
||
bool is_empty() const;
|
||
|
||
+ void write_to(bm_word_t* buffer, size_t buffer_size_in_bytes) const;
|
||
void print_on_error(outputStream* st, const char* prefix) const;
|
||
|
||
#ifndef PRODUCT
|
||
diff --git a/hotspot/src/share/vm/utilities/constantTag.hpp b/hotspot/src/share/vm/utilities/constantTag.hpp
|
||
index ae99d5706..07a873743 100644
|
||
--- a/hotspot/src/share/vm/utilities/constantTag.hpp
|
||
+++ b/hotspot/src/share/vm/utilities/constantTag.hpp
|
||
@@ -43,7 +43,8 @@ enum {
|
||
JVM_CONSTANT_UnresolvedClassInError = 103, // Error tag due to resolution error
|
||
JVM_CONSTANT_MethodHandleInError = 104, // Error tag due to resolution error
|
||
JVM_CONSTANT_MethodTypeInError = 105, // Error tag due to resolution error
|
||
- JVM_CONSTANT_InternalMax = 105 // Last implementation tag
|
||
+ JVM_CONSTANT_ReplacedSymbol = 106,
|
||
+ JVM_CONSTANT_InternalMax = 106 // Last implementation tag
|
||
};
|
||
|
||
|
||
@@ -62,7 +63,7 @@ class constantTag VALUE_OBJ_CLASS_SPEC {
|
||
bool is_double() const { return _tag == JVM_CONSTANT_Double; }
|
||
bool is_name_and_type() const { return _tag == JVM_CONSTANT_NameAndType; }
|
||
bool is_utf8() const { return _tag == JVM_CONSTANT_Utf8; }
|
||
-
|
||
+ bool is_replaced_symbol() const { return _tag == JVM_CONSTANT_ReplacedSymbol; }
|
||
bool is_invalid() const { return _tag == JVM_CONSTANT_Invalid; }
|
||
|
||
bool is_unresolved_klass() const {
|
||
diff --git a/hotspot/src/share/vm/utilities/globalDefinitions.hpp b/hotspot/src/share/vm/utilities/globalDefinitions.hpp
|
||
index 81866b840..25f6f026c 100644
|
||
--- a/hotspot/src/share/vm/utilities/globalDefinitions.hpp
|
||
+++ b/hotspot/src/share/vm/utilities/globalDefinitions.hpp
|
||
@@ -1511,6 +1511,16 @@ static inline void* dereference_vptr(const void* addr) {
|
||
return *(void**)addr;
|
||
}
|
||
|
||
+
|
||
+template<typename K> unsigned primitive_hash(const K& k) {
|
||
+ unsigned hash = (unsigned)((uintptr_t)k);
|
||
+ return hash ^ (hash >> 3); // just in case we're dealing with aligned ptrs
|
||
+}
|
||
+
|
||
+template<typename K> bool primitive_equals(const K& k0, const K& k1) {
|
||
+ return k0 == k1;
|
||
+}
|
||
+
|
||
#ifndef PRODUCT
|
||
|
||
// For unit testing only
|
||
@@ -1519,7 +1529,6 @@ public:
|
||
static void test_globals();
|
||
static void test_proper_unit();
|
||
};
|
||
-
|
||
#endif // PRODUCT
|
||
|
||
#endif // SHARE_VM_UTILITIES_GLOBALDEFINITIONS_HPP
|
||
diff --git a/hotspot/src/share/vm/utilities/hashtable.cpp b/hotspot/src/share/vm/utilities/hashtable.cpp
|
||
index c026e6a0e..66df8f1f8 100644
|
||
--- a/hotspot/src/share/vm/utilities/hashtable.cpp
|
||
+++ b/hotspot/src/share/vm/utilities/hashtable.cpp
|
||
@@ -34,7 +34,7 @@
|
||
#include "utilities/hashtable.hpp"
|
||
#include "utilities/hashtable.inline.hpp"
|
||
#include "utilities/numberSeq.hpp"
|
||
-
|
||
+#include "utilities/align.hpp"
|
||
|
||
// This hashtable is implemented as an open hash table with a fixed number of buckets.
|
||
|
||
@@ -145,7 +145,7 @@ template <MEMFLAGS F> void BasicHashtable<F>::free_buckets() {
|
||
// Don't delete the buckets in the shared space. They aren't
|
||
// allocated by os::malloc
|
||
if (!UseSharedSpaces ||
|
||
- !FileMapInfo::current_info()->is_in_shared_space(_buckets)) {
|
||
+ !MetaspaceShared::is_in_shared_space(_buckets)) {
|
||
FREE_C_HEAP_ARRAY(HashtableBucket, _buckets, F);
|
||
}
|
||
_buckets = NULL;
|
||
@@ -221,7 +221,7 @@ template <MEMFLAGS F> void BasicHashtable<F>::copy_table(char** top, char* end)
|
||
*top += entry_size();
|
||
}
|
||
}
|
||
- *plen = (char*)(*top) - (char*)plen - sizeof(*plen);
|
||
+ *plen = ((char*)(*top) - (char*)plen) - sizeof(*plen);
|
||
|
||
// Set the shared bit.
|
||
|
||
@@ -317,7 +317,6 @@ template <class T, MEMFLAGS F> void RehashableHashtable<T, F>::dump_table(output
|
||
st->print_cr("Maximum bucket size : %9d", (int)summary.maximum());
|
||
}
|
||
|
||
-
|
||
// Dump the hash table buckets.
|
||
|
||
template <MEMFLAGS F> void BasicHashtable<F>::copy_buckets(char** top, char* end) {
|
||
@@ -335,6 +334,57 @@ template <MEMFLAGS F> void BasicHashtable<F>::copy_buckets(char** top, char* end
|
||
*top += len;
|
||
}
|
||
|
||
+template <MEMFLAGS F> bool BasicHashtable<F>::resize(int new_size) {
|
||
+
|
||
+ // Allocate new buckets
|
||
+ HashtableBucket<F>* buckets_new = NEW_C_HEAP_ARRAY2_RETURN_NULL(HashtableBucket<F>, new_size, F, CURRENT_PC);
|
||
+ if (buckets_new == NULL) {
|
||
+ return false;
|
||
+ }
|
||
+
|
||
+ // Clear the new buckets
|
||
+ for (int i = 0; i < new_size; i++) {
|
||
+ buckets_new[i].clear();
|
||
+ }
|
||
+
|
||
+ int table_size_old = _table_size;
|
||
+ // hash_to_index() uses _table_size, so switch the sizes now
|
||
+ _table_size = new_size;
|
||
+
|
||
+ // Move entries from the old table to a new table
|
||
+ for (int index_old = 0; index_old < table_size_old; index_old++) {
|
||
+ for (BasicHashtableEntry<F>* p = _buckets[index_old].get_entry(); p != NULL; ) {
|
||
+ BasicHashtableEntry<F>* next = p->next();
|
||
+ int index_new = hash_to_index(p->hash());
|
||
+
|
||
+ p->set_next(buckets_new[index_new].get_entry());
|
||
+ buckets_new[index_new].set_entry(p);
|
||
+ p = next;
|
||
+ }
|
||
+ }
|
||
+
|
||
+ // The old backets now can be released
|
||
+ BasicHashtable<F>::free_buckets();
|
||
+
|
||
+ // Switch to the new storage
|
||
+ _buckets = buckets_new;
|
||
+
|
||
+ return true;
|
||
+}
|
||
+
|
||
+template <MEMFLAGS F> bool BasicHashtable<F>::maybe_grow(int max_size, int load_factor) {
|
||
+ assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint");
|
||
+
|
||
+ if (table_size() >= max_size) {
|
||
+ return false;
|
||
+ }
|
||
+ if (number_of_entries() / table_size() > load_factor) {
|
||
+ resize(MIN2<int>(table_size() * 2, max_size));
|
||
+ return true;
|
||
+ } else {
|
||
+ return false;
|
||
+ }
|
||
+}
|
||
|
||
#ifndef PRODUCT
|
||
|
||
@@ -352,7 +402,6 @@ template <class T, MEMFLAGS F> void Hashtable<T, F>::print() {
|
||
}
|
||
}
|
||
|
||
-
|
||
template <MEMFLAGS F> void BasicHashtable<F>::verify() {
|
||
int count = 0;
|
||
for (int i = 0; i < table_size(); i++) {
|
||
@@ -406,3 +455,4 @@ template class BasicHashtable<mtClass>;
|
||
template class BasicHashtable<mtSymbol>;
|
||
template class BasicHashtable<mtCode>;
|
||
template class BasicHashtable<mtInternal>;
|
||
+template class BasicHashtable<mtClassShared>;
|
||
diff --git a/hotspot/src/share/vm/utilities/hashtable.hpp b/hotspot/src/share/vm/utilities/hashtable.hpp
|
||
index 30e442d15..358b09c3d 100644
|
||
--- a/hotspot/src/share/vm/utilities/hashtable.hpp
|
||
+++ b/hotspot/src/share/vm/utilities/hashtable.hpp
|
||
@@ -151,7 +151,7 @@ public:
|
||
void copy_table(char** top, char* end);
|
||
|
||
// Bucket handling
|
||
- int hash_to_index(unsigned int full_hash) {
|
||
+ int hash_to_index(unsigned int full_hash) const {
|
||
int h = full_hash % _table_size;
|
||
assert(h >= 0 && h < _table_size, "Illegal hash value");
|
||
return h;
|
||
@@ -184,7 +184,7 @@ protected:
|
||
int entry_size() const { return _entry_size; }
|
||
|
||
// The following method is MT-safe and may be used with caution.
|
||
- BasicHashtableEntry<F>* bucket(int i);
|
||
+ BasicHashtableEntry<F>* bucket(int i) const;
|
||
|
||
// The following method is not MT-safe and must be done under lock.
|
||
BasicHashtableEntry<F>** bucket_addr(int i) { return _buckets[i].entry_addr(); }
|
||
@@ -234,7 +234,7 @@ protected:
|
||
// is mt-safe wrt. to other calls of this method.
|
||
void bulk_free_entries(BucketUnlinkContext* context);
|
||
public:
|
||
- int table_size() { return _table_size; }
|
||
+ int table_size() const { return _table_size; }
|
||
void set_entry(int index, BasicHashtableEntry<F>* entry);
|
||
|
||
void add_entry(int index, BasicHashtableEntry<F>* entry);
|
||
@@ -243,6 +243,10 @@ public:
|
||
|
||
int number_of_entries() { return _number_of_entries; }
|
||
|
||
+ bool resize(int new_size);
|
||
+
|
||
+ bool maybe_grow(int max_size, int load_factor = 0);
|
||
+
|
||
void verify() PRODUCT_RETURN;
|
||
};
|
||
|
||
@@ -364,4 +368,92 @@ public:
|
||
}
|
||
};
|
||
|
||
+// A subclass of BasicHashtable that allows you to do a simple K -> V mapping
|
||
+// without using tons of boilerplate code.
|
||
+template<
|
||
+ typename K, typename V, MEMFLAGS F,
|
||
+ unsigned (*HASH) (K const&) = primitive_hash<K>,
|
||
+ bool (*EQUALS)(K const&, K const&) = primitive_equals<K>
|
||
+ >
|
||
+class KVHashtable : public BasicHashtable<F> {
|
||
+ class KVHashtableEntry : public BasicHashtableEntry<F> {
|
||
+ public:
|
||
+ K _key;
|
||
+ V _value;
|
||
+ KVHashtableEntry* next() {
|
||
+ return (KVHashtableEntry*)BasicHashtableEntry<F>::next();
|
||
+ }
|
||
+ };
|
||
+
|
||
+protected:
|
||
+ KVHashtableEntry* bucket(int i) const {
|
||
+ return (KVHashtableEntry*)BasicHashtable<F>::bucket(i);
|
||
+ }
|
||
+
|
||
+ KVHashtableEntry* new_entry(unsigned int hashValue, K key, V value) {
|
||
+ KVHashtableEntry* entry = (KVHashtableEntry*)BasicHashtable<F>::new_entry(hashValue);
|
||
+ entry->_key = key;
|
||
+ entry->_value = value;
|
||
+ return entry;
|
||
+ }
|
||
+
|
||
+public:
|
||
+ KVHashtable(int table_size) : BasicHashtable<F>(table_size, sizeof(KVHashtableEntry)) {}
|
||
+
|
||
+ V* add(K key, V value) {
|
||
+ unsigned int hash = HASH(key);
|
||
+ KVHashtableEntry* entry = new_entry(hash, key, value);
|
||
+ BasicHashtable<F>::add_entry(BasicHashtable<F>::hash_to_index(hash), entry);
|
||
+ return &(entry->_value);
|
||
+ }
|
||
+
|
||
+ V* lookup(K key) const {
|
||
+ unsigned int hash = HASH(key);
|
||
+ int index = BasicHashtable<F>::hash_to_index(hash);
|
||
+ for (KVHashtableEntry* e = bucket(index); e != NULL; e = e->next()) {
|
||
+ if (e->hash() == hash && EQUALS(e->_key, key)) {
|
||
+ return &(e->_value);
|
||
+ }
|
||
+ }
|
||
+ return NULL;
|
||
+ }
|
||
+
|
||
+ // Look up the key.
|
||
+ // If an entry for the key exists, leave map unchanged and return a pointer to its value.
|
||
+ // If no entry for the key exists, create a new entry from key and value and return a
|
||
+ // pointer to the value.
|
||
+ // *p_created is true if entry was created, false if entry pre-existed.
|
||
+ V* add_if_absent(K key, V value, bool* p_created) {
|
||
+ unsigned int hash = HASH(key);
|
||
+ int index = BasicHashtable<F>::hash_to_index(hash);
|
||
+ for (KVHashtableEntry* e = bucket(index); e != NULL; e = e->next()) {
|
||
+ if (e->hash() == hash && EQUALS(e->_key, key)) {
|
||
+ *p_created = false;
|
||
+ return &(e->_value);
|
||
+ }
|
||
+ }
|
||
+ KVHashtableEntry* entry = new_entry(hash, key, value);
|
||
+ BasicHashtable<F>::add_entry(BasicHashtable<F>::hash_to_index(hash), entry);
|
||
+ *p_created = true;
|
||
+ return &(entry->_value);
|
||
+ }
|
||
+
|
||
+ int table_size() const {
|
||
+ return BasicHashtable<F>::table_size();
|
||
+ }
|
||
+
|
||
+ // ITER contains bool do_entry(K, V const&), which will be
|
||
+ // called for each entry in the table. If do_entry() returns false,
|
||
+ // the iteration is cancelled.
|
||
+ template<class ITER>
|
||
+ void iterate(ITER* iter) const {
|
||
+ for (int index = 0; index < table_size(); index++) {
|
||
+ for (KVHashtableEntry* e = bucket(index); e != NULL; e = e->next()) {
|
||
+ bool cont = iter->do_entry(e->_key, &e->_value);
|
||
+ if (!cont) { return; }
|
||
+ }
|
||
+ }
|
||
+ }
|
||
+};
|
||
+
|
||
#endif // SHARE_VM_UTILITIES_HASHTABLE_HPP
|
||
diff --git a/hotspot/src/share/vm/utilities/hashtable.inline.hpp b/hotspot/src/share/vm/utilities/hashtable.inline.hpp
|
||
index 9356c985e..ee22ba835 100644
|
||
--- a/hotspot/src/share/vm/utilities/hashtable.inline.hpp
|
||
+++ b/hotspot/src/share/vm/utilities/hashtable.inline.hpp
|
||
@@ -72,7 +72,7 @@ template <MEMFLAGS F> inline void BasicHashtable<F>::initialize(int table_size,
|
||
|
||
|
||
// The following method is MT-safe and may be used with caution.
|
||
-template <MEMFLAGS F> inline BasicHashtableEntry<F>* BasicHashtable<F>::bucket(int i) {
|
||
+template <MEMFLAGS F> inline BasicHashtableEntry<F>* BasicHashtable<F>::bucket(int i) const {
|
||
return _buckets[i].get_entry();
|
||
}
|
||
|
||
diff --git a/hotspot/src/share/vm/utilities/ostream.cpp b/hotspot/src/share/vm/utilities/ostream.cpp
|
||
index fa199a235..14d82ad0f 100644
|
||
--- a/hotspot/src/share/vm/utilities/ostream.cpp
|
||
+++ b/hotspot/src/share/vm/utilities/ostream.cpp
|
||
@@ -379,6 +379,7 @@ xmlStream* xtty;
|
||
outputStream* tty;
|
||
outputStream* gclog_or_tty;
|
||
CDS_ONLY(jsaFileStream* classlist_file;) // Only dump the classes that can be stored into the CDS archive
|
||
+CDS_ONLY(outputStream* dynamic_cds_log;)
|
||
extern Mutex* tty_lock;
|
||
|
||
#define EXTRACHARLEN 32
|
||
@@ -1402,6 +1403,16 @@ void ostream_init_log() {
|
||
jsaFileStream(list_name);
|
||
FREE_C_HEAP_ARRAY(char, list_name, mtInternal);
|
||
}
|
||
+
|
||
+ // For -XX:DynamicCDSLog=<file> option
|
||
+ if (DynamicCDSLog != NULL) {
|
||
+ const char* log_name = make_log_name(DynamicCDSLog, NULL);
|
||
+ dynamic_cds_log = new(ResourceObj::C_HEAP, mtInternal)
|
||
+ fileStream(log_name);
|
||
+ FREE_C_HEAP_ARRAY(char, log_name, mtInternal);
|
||
+ } else {
|
||
+ dynamic_cds_log = tty;
|
||
+ }
|
||
#endif
|
||
|
||
// If we haven't lazily initialized the logfile yet, do it now,
|
||
diff --git a/hotspot/src/share/vm/utilities/ostream.hpp b/hotspot/src/share/vm/utilities/ostream.hpp
|
||
index c69289fb5..d0f9aac57 100644
|
||
--- a/hotspot/src/share/vm/utilities/ostream.hpp
|
||
+++ b/hotspot/src/share/vm/utilities/ostream.hpp
|
||
@@ -221,7 +221,7 @@ class jsaFileStream : public fileStream {
|
||
};
|
||
|
||
CDS_ONLY(extern jsaFileStream* classlist_file;)
|
||
-
|
||
+CDS_ONLY(extern outputStream* dynamic_cds_log;)
|
||
// unlike fileStream, fdStream does unbuffered I/O by calling
|
||
// open() and write() directly. It is async-safe, but output
|
||
// from multiple thread may be mixed together. Used by fatal
|
||
diff --git a/hotspot/src/share/vm/utilities/resourceHash.hpp b/hotspot/src/share/vm/utilities/resourceHash.hpp
|
||
index 82c1219b4..941f25996 100644
|
||
--- a/hotspot/src/share/vm/utilities/resourceHash.hpp
|
||
+++ b/hotspot/src/share/vm/utilities/resourceHash.hpp
|
||
@@ -27,21 +27,13 @@
|
||
|
||
#include "memory/allocation.hpp"
|
||
#include "utilities/top.hpp"
|
||
+#include "utilities/globalDefinitions.hpp"
|
||
|
||
template<typename K> struct ResourceHashtableFns {
|
||
typedef unsigned (*hash_fn)(K const&);
|
||
typedef bool (*equals_fn)(K const&, K const&);
|
||
};
|
||
|
||
-template<typename K> unsigned primitive_hash(const K& k) {
|
||
- unsigned hash = (unsigned)((uintptr_t)k);
|
||
- return hash ^ (hash >> 3); // just in case we're dealing with aligned ptrs
|
||
-}
|
||
-
|
||
-template<typename K> bool primitive_equals(const K& k0, const K& k1) {
|
||
- return k0 == k1;
|
||
-}
|
||
-
|
||
template<
|
||
typename K, typename V,
|
||
// xlC does not compile this:
|
||
@@ -66,6 +58,10 @@ class ResourceHashtable : public ResourceObj {
|
||
|
||
Node(unsigned hash, K const& key, V const& value) :
|
||
_hash(hash), _key(key), _value(value), _next(NULL) {}
|
||
+
|
||
+ // Create a node with a default-constructed value.
|
||
+ Node(unsigned hash, K const& key) :
|
||
+ _hash(hash), _key(key), _value(), _next(NULL) {}
|
||
};
|
||
|
||
Node* _table[SIZE];
|
||
@@ -139,6 +135,19 @@ class ResourceHashtable : public ResourceObj {
|
||
}
|
||
}
|
||
|
||
+ V* put_if_absent(K const& key, bool* p_created) {
|
||
+ unsigned hv = HASH(key);
|
||
+ Node** ptr = lookup_node(hv, key);
|
||
+ if (*ptr == NULL) {
|
||
+ *ptr = new (ALLOC_TYPE, MEM_TYPE) Node(hv, key);
|
||
+ *p_created = true;
|
||
+ } else {
|
||
+ *p_created = false;
|
||
+ }
|
||
+ return &(*ptr)->_value;
|
||
+ }
|
||
+
|
||
+
|
||
bool remove(K const& key) {
|
||
unsigned hv = HASH(key);
|
||
Node** ptr = lookup_node(hv, key);
|
||
--
|
||
2.17.1
|
||
|