diff --git a/Dynamic-CDS-Archive.patch b/Dynamic-CDS-Archive.patch new file mode 100644 index 0000000..edccdea --- /dev/null +++ b/Dynamic-CDS-Archive.patch @@ -0,0 +1,8657 @@ +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(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(4 * K, mtClassShared); ++ _symbols = new (ResourceObj::C_HEAP, mtClassShared) GrowableArray(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* _objs; // Source objects to be archived ++ BitMap _ptrmap; // Marks the addresses of the pointer fields ++ // in the source objects ++ public: ++ SourceObjList(); ++ ~SourceObjList(); ++ GrowableArray* 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 _src_obj_table; ++ GrowableArray* _klasses; ++ GrowableArray* _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 bool is_in_buffer_space(T obj) const { ++ return is_in_buffer_space(address(obj)); ++ } ++ ++ template bool is_in_requested_static_archive(T p) const { ++ return _requested_static_archive_bottom <= (address)p && (address)p < _requested_static_archive_top; ++ } ++ ++ template bool is_in_mapped_static_archive(T p) const { ++ return _mapped_static_archive_bottom <= (address)p && (address)p < _mapped_static_archive_top; ++ } ++ ++ template 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 ++ 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 ++ 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 ++ static Array* new_ro_array(int length) { ++ size_t byte_size = Array::byte_sizeof(length); ++ Array* array = (Array*)ro_region_alloc(byte_size); ++ array->initialize(length); ++ return array; ++ } ++ ++ template ++ static Array* new_rw_array(int length) { ++ size_t byte_size = Array::byte_sizeof(length); ++ Array* array = (Array*)rw_region_alloc(byte_size); ++ array->initialize(length); ++ return array; ++ } ++ ++ template ++ static size_t ro_array_bytesize(int length) { ++ size_t byte_size = Array::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* klasses() const { return _klasses; } ++ GrowableArray* 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 ++ static void mark_pointer(T* ptr_loc) { ++ mark_pointer((address*)ptr_loc); ++ } ++ ++ template ++ 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* 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 ++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 marker; ++ k->metaspace_pointers_do(&marker); ++ marker.finish(); ++ } else { ++ PointerRemarker 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*, _num_buckets, mtSymbol); ++ for (int i = 0; i < _num_buckets; i++) { ++ _buckets[i] = new (ResourceObj::C_HEAP, mtSymbol) GrowableArray(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* bucket = _buckets[index]; ++ delete bucket; ++ } ++ ++ FREE_C_HEAP_ARRAY(GrowableArray*, _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(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(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* 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(_num_buckets + 1); ++ _compact_entries = ArchiveBuilder::new_ro_array(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* 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; iat(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** _buckets; ++ CompactHashtableStats* _stats; ++ Array* _compact_buckets; ++ Array* _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[] ++// ++// 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 ++ 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 ++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, 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) { +@@ -225,10 +241,25 @@ Symbol* SymbolTable::lookup(int index, const char* name, + unsigned int SymbolTable::hash_symbol(const char* s, int len) { + return use_alternate_hashcode() ? + AltHashing::halfsiphash_32(seed(), (const uint8_t*)s, len) : +- java_lang_String::hash_code(s, len); ++ 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 +@@ -236,12 +267,32 @@ unsigned int SymbolTable::hash_symbol(const char* s, int len) { + // entries in the symbol table during normal execution (only during + // safepoints). + +-Symbol* SymbolTable::lookup(const char* name, int len, TRAPS) { ++Symbol* SymbolTable::lookup_common(const char* name, int 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* 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* 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* symbols, ++ CompactHashtableWriter* ch_table); ++public: + static void copy_buckets(char** top, char*end) { + the_table()->Hashtable::copy_buckets(top, end); + } +@@ -246,6 +255,9 @@ public: + static void reverse(void* boundary = NULL) { + the_table()->Hashtable::reverse(boundary); + } ++ static size_t estimate_size_for_archive(); ++ static void write_to_archive(GrowableArray* 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 { ++ 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* _verifier_constraints; ++ GrowableArray* _verifier_constraint_flags; ++ GrowableArray* _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(k); ++} ++ ++class DumpTimeSharedClassTable: public ResourceHashtable< ++ InstanceKlass*, ++ DumpTimeSharedClassInfo, ++ &DumpTimeSharedClassTable_hash, ++ primitive_equals, ++ 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 ++ // 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* 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* 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(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 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 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 ++ 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(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 void CHeapObj::operator delete [](void* p){ + } + + template +-E* ArrayAllocator::allocate(size_t length) { +- assert(_addr == NULL, "Already in use"); ++char* ArrayAllocator::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 ++E* ArrayAllocator::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 ++E* ArrayAllocator::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 + void ArrayAllocator::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 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(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 { + 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 { ++ 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* 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* foo() { return (Array*)_obj; } ++ // Symbol* bar() { return (Symbol*) _obj; } ++ // ++ // [2] All Array dimensions are statically declared. ++ class Ref : public CHeapObj { ++ 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 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 ArrayRef : public Ref { ++ Array** _mpp; ++ protected: ++ Array* dereference() const { ++ return *_mpp; ++ } ++ virtual void** mpp() const { ++ return (void**)_mpp; ++ } ++ ++ ArrayRef(Array** 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, 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 OtherArrayRef : public ArrayRef { ++ public: ++ OtherArrayRef(Array** mpp, Writability w) : ArrayRef(mpp, w) {} ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ Array* array = ArrayRef::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* array = (Array*)new_loc; ++ if (TraceDynamicCDS) ++ dynamic_cds_log->print_cr("Iter(OtherArray): %p [%d]", array, array->length()); ++ } ++ }; ++ ++ // MSOArrayRef -- iterate an instance of Array, where T is a subtype of MetaspaceObj. ++ // We recursively call T::metaspace_pointers_do() for each element in this array. ++ template class MSOArrayRef : public ArrayRef { ++ public: ++ MSOArrayRef(Array** mpp, Writability w) : ArrayRef(mpp, w) {} ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ metaspace_pointers_do_at_impl(it, ArrayRef::dereference()); ++ } ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { ++ metaspace_pointers_do_at_impl(it, (Array*)new_loc); ++ } ++ private: ++ void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array* 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, where T is a subtype of MetaspaceObj. ++ // We recursively call MetaspaceClosure::push() for each pointer in this array. ++ template class MSOPointerArrayRef : public ArrayRef { ++ public: ++ MSOPointerArrayRef(Array** mpp, Writability w) : ArrayRef(mpp, w) {} ++ ++ virtual void metaspace_pointers_do(MetaspaceClosure *it) const { ++ metaspace_pointers_do_at_impl(it, ArrayRef::dereference()); ++ } ++ virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const { ++ metaspace_pointers_do_at_impl(it, (Array*)new_loc); ++ } ++ private: ++ void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array* 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 ++ virtual bool do_ref(Ref* ref, bool read_only) = 0; ++ ++private: ++ template ++ 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* a1 = ...; it->push(&a1); => OtherArrayRef ++ // Array* a2 = ...; it->push(&a2); => MSOArrayRef ++ // Array* a3 = ...; it->push(&a3); => MSOPointerArrayRef ++ // Array*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef ++ // Array* 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* a6 = ...; it->push(&a6); => Hashtable is not a subclass of MetaspaceObj ++ // Array* a7 = ...; it->push(&a7); => int is not a subclass of MetaspaceObj ++ ++ template ++ void push(T** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ template ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(mpp, w); ++ } ++ ++ template ++ void push(Array** mpp, Writability w = _default) { ++ push_with_ref >(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* h = NULL; ++ it->push(&h); ++ ++ Array*>* a6 = NULL; ++ it->push(&a6); ++ ++ Array* 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 _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* 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 AnnotationArray; + +@@ -54,6 +55,8 @@ class Annotations: public MetaspaceObj { + Array* _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* 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; iverify_just_initialized(f2_used[i]); ++ }) ++ } else { ++ for (int i=0; ireinitialize(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* 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* methods, Symbol* name, Symbol* signatur + } + #endif + ++bool InstanceKlass::_disable_method_binary_search = false; ++ ++NOINLINE int linear_search(const Array* 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* 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* 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_castincrement_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::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* methods, bool idempotent, bool set_idnums) { ++void Method::sort_methods(Array* 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(methods->data(), length, method_comparator, idempotent); ++ QuickSort::sort(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* methods, bool idempotent = false, bool set_idnums = true); ++ static void sort_methods(Array* 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 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(full_export, true, false)); + #if INCLUDE_SERVICES // Heap dumping/inspection supported + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false)); ++ DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(DCmd_Source_Internal | DCmd_Source_AttachAPI, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(full_export, true, false)); + DCmdFactory::register_DCmdFactory(new DCmdFactoryImpl(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 + 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 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 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 void BasicHashtable::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 void BasicHashtable::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 void RehashableHashtable::dump_table(output + st->print_cr("Maximum bucket size : %9d", (int)summary.maximum()); + } + +- + // Dump the hash table buckets. + + template void BasicHashtable::copy_buckets(char** top, char* end) { +@@ -335,6 +334,57 @@ template void BasicHashtable::copy_buckets(char** top, char* end + *top += len; + } + ++template bool BasicHashtable::resize(int new_size) { ++ ++ // Allocate new buckets ++ HashtableBucket* buckets_new = NEW_C_HEAP_ARRAY2_RETURN_NULL(HashtableBucket, 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* p = _buckets[index_old].get_entry(); p != NULL; ) { ++ BasicHashtableEntry* 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::free_buckets(); ++ ++ // Switch to the new storage ++ _buckets = buckets_new; ++ ++ return true; ++} ++ ++template bool BasicHashtable::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(table_size() * 2, max_size)); ++ return true; ++ } else { ++ return false; ++ } ++} + + #ifndef PRODUCT + +@@ -352,7 +402,6 @@ template void Hashtable::print() { + } + } + +- + template void BasicHashtable::verify() { + int count = 0; + for (int i = 0; i < table_size(); i++) { +@@ -406,3 +455,4 @@ template class BasicHashtable; + template class BasicHashtable; + template class BasicHashtable; + template class BasicHashtable; ++template class BasicHashtable; +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* bucket(int i); ++ BasicHashtableEntry* bucket(int i) const; + + // The following method is not MT-safe and must be done under lock. + BasicHashtableEntry** 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* entry); + + void add_entry(int index, BasicHashtableEntry* 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, ++ bool (*EQUALS)(K const&, K const&) = primitive_equals ++ > ++class KVHashtable : public BasicHashtable { ++ class KVHashtableEntry : public BasicHashtableEntry { ++ public: ++ K _key; ++ V _value; ++ KVHashtableEntry* next() { ++ return (KVHashtableEntry*)BasicHashtableEntry::next(); ++ } ++ }; ++ ++protected: ++ KVHashtableEntry* bucket(int i) const { ++ return (KVHashtableEntry*)BasicHashtable::bucket(i); ++ } ++ ++ KVHashtableEntry* new_entry(unsigned int hashValue, K key, V value) { ++ KVHashtableEntry* entry = (KVHashtableEntry*)BasicHashtable::new_entry(hashValue); ++ entry->_key = key; ++ entry->_value = value; ++ return entry; ++ } ++ ++public: ++ KVHashtable(int table_size) : BasicHashtable(table_size, sizeof(KVHashtableEntry)) {} ++ ++ V* add(K key, V value) { ++ unsigned int hash = HASH(key); ++ KVHashtableEntry* entry = new_entry(hash, key, value); ++ BasicHashtable::add_entry(BasicHashtable::hash_to_index(hash), entry); ++ return &(entry->_value); ++ } ++ ++ V* lookup(K key) const { ++ unsigned int hash = HASH(key); ++ int index = BasicHashtable::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::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::add_entry(BasicHashtable::hash_to_index(hash), entry); ++ *p_created = true; ++ return &(entry->_value); ++ } ++ ++ int table_size() const { ++ return BasicHashtable::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 ++ 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 inline void BasicHashtable::initialize(int table_size, + + + // The following method is MT-safe and may be used with caution. +-template inline BasicHashtableEntry* BasicHashtable::bucket(int i) { ++template inline BasicHashtableEntry* BasicHashtable::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= 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 struct ResourceHashtableFns { + typedef unsigned (*hash_fn)(K const&); + typedef bool (*equals_fn)(K const&, K const&); + }; + +-template 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 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 + diff --git a/openjdk-1.8.0.spec b/openjdk-1.8.0.spec index 99e4e66..7da47f7 100644 --- a/openjdk-1.8.0.spec +++ b/openjdk-1.8.0.spec @@ -916,7 +916,7 @@ Provides: java-%{javaver}-%{origin}-accessibility%{?1} = %{epoch}:%{version}-%{r Name: java-%{javaver}-%{origin} Version: %{javaver}.%{updatever}.%{buildver} -Release: 5 +Release: 6 # java-1.5.0-ibm from jpackage.org set Epoch to 1 for unknown reasons # and this change was brought into RHEL-4. java-1.5.0-ibm packages # also included the epoch in their virtual provides. This created a @@ -1141,6 +1141,7 @@ Patch251: fix_wrap_memcpy_undefined_gcc10_3.patch Patch252: 8290705_fix_StringConcat_validate_mem_flow_asserts_with_unexpected_userStoreI.patch Patch253: 8143925-enhancing-CounterMode.crypt-for-AESCrypt.patch Patch254: kae-usability-enhancement.patch +Patch255: Dynamic-CDS-Archive.patch ############################################# # @@ -1622,6 +1623,7 @@ pushd %{top_level_dir_name} %patch252 -p1 %patch253 -p1 %patch254 -p1 +%patch255 -p1 popd # System library fixes @@ -2246,6 +2248,9 @@ cjc.mainProgram(arg) %endif %changelog +* Thu Sep 15 2022 kuenking111 - 1:1.8.0.342-b07.6 +- add Dynamic-CDS-Archive.patch + * Thu Sep 15 2022 kuenking111 - 1:1.8.0.342-b07.5 - add kae-usability-enhancement.patch