8374549: Extend MetaspaceClosure to cover non-MetaspaceObj types

Reviewed-by: kvn, asmehra
This commit is contained in:
Ioi Lam 2026-01-27 03:16:43 +00:00
parent fdcc122a9d
commit cba7d88ca4
30 changed files with 864 additions and 517 deletions

View File

@ -0,0 +1,34 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#include "cds/aotGrowableArray.hpp"
#include "cds/aotMetaspace.hpp"
#include "memory/allocation.inline.hpp"
#include "utilities/growableArray.hpp"
void AOTGrowableArrayHelper::deallocate(void* mem) {
if (!AOTMetaspace::in_aot_cache(mem)) {
GrowableArrayCHeapAllocator::deallocate(mem);
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_AOT_AOTGROWABLEARRAY_HPP
#define SHARE_AOT_AOTGROWABLEARRAY_HPP
#include <memory/metaspaceClosureType.hpp>
#include <utilities/growableArray.hpp>
class AOTGrowableArrayHelper {
public:
static void deallocate(void* mem);
};
// An AOTGrowableArray<T> provides the same functionality as a GrowableArray<T> that
// uses the C heap allocator. In addition, AOTGrowableArray<T> can be iterated with
// MetaspaceClosure. This type should be used for growable arrays that need to be
// stored in the AOT cache. See ModuleEntry::_reads for an example.
template <typename E>
class AOTGrowableArray : public GrowableArrayWithAllocator<E, AOTGrowableArray<E>> {
friend class VMStructs;
friend class GrowableArrayWithAllocator<E, AOTGrowableArray>;
static E* allocate(int max, MemTag mem_tag) {
return (E*)GrowableArrayCHeapAllocator::allocate(max, sizeof(E), mem_tag);
}
E* allocate() {
return allocate(this->_capacity, mtClass);
}
void deallocate(E* mem) {
#if INCLUDE_CDS
AOTGrowableArrayHelper::deallocate(mem);
#else
GrowableArrayCHeapAllocator::deallocate(mem);
#endif
}
public:
AOTGrowableArray(int initial_capacity, MemTag mem_tag) :
GrowableArrayWithAllocator<E, AOTGrowableArray>(
allocate(initial_capacity, mem_tag),
initial_capacity) {}
AOTGrowableArray() : AOTGrowableArray(0, mtClassShared) {}
// methods required by MetaspaceClosure
void metaspace_pointers_do(MetaspaceClosure* it);
int size_in_heapwords() const { return (int)heap_word_size(sizeof(*this)); }
MetaspaceClosureType type() const { return MetaspaceClosureType::GrowableArrayType; }
static bool is_read_only_by_default() { return false; }
};
#endif // SHARE_AOT_AOTGROWABLEARRAY_HPP

View File

@ -0,0 +1,37 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP
#define SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP
#include "cds/aotGrowableArray.hpp"
#include "memory/metaspaceClosure.hpp"
template <typename E>
void AOTGrowableArray<E>::metaspace_pointers_do(MetaspaceClosure* it) {
it->push_c_array(AOTGrowableArray<E>::data_addr(), AOTGrowableArray<E>::capacity());
}
#endif // SHARE_CDS_AOTGROWABLEARRAY_INLINE_HPP

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -29,6 +29,8 @@
#include "cds/aotStreamedHeapWriter.hpp" #include "cds/aotStreamedHeapWriter.hpp"
#include "cds/cdsConfig.hpp" #include "cds/cdsConfig.hpp"
#include "cds/filemap.hpp" #include "cds/filemap.hpp"
#include "classfile/moduleEntry.hpp"
#include "classfile/packageEntry.hpp"
#include "classfile/systemDictionaryShared.hpp" #include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmClasses.hpp" #include "classfile/vmClasses.hpp"
#include "logging/log.hpp" #include "logging/log.hpp"
@ -141,7 +143,7 @@ public:
info._buffered_addr = ref->obj(); info._buffered_addr = ref->obj();
info._requested_addr = ref->obj(); info._requested_addr = ref->obj();
info._bytes = ref->size() * BytesPerWord; info._bytes = ref->size() * BytesPerWord;
info._type = ref->msotype(); info._type = ref->type();
_objs.append(info); _objs.append(info);
} }
@ -214,7 +216,7 @@ void AOTMapLogger::dumptime_log_metaspace_region(const char* name, DumpRegion* r
info._buffered_addr = src_info->buffered_addr(); info._buffered_addr = src_info->buffered_addr();
info._requested_addr = info._buffered_addr + _buffer_to_requested_delta; info._requested_addr = info._buffered_addr + _buffer_to_requested_delta;
info._bytes = src_info->size_in_bytes(); info._bytes = src_info->size_in_bytes();
info._type = src_info->msotype(); info._type = src_info->type();
objs.append(info); objs.append(info);
} }
@ -332,43 +334,52 @@ void AOTMapLogger::log_metaspace_objects_impl(address region_base, address regio
address buffered_addr = info._buffered_addr; address buffered_addr = info._buffered_addr;
address requested_addr = info._requested_addr; address requested_addr = info._requested_addr;
int bytes = info._bytes; int bytes = info._bytes;
MetaspaceObj::Type type = info._type; MetaspaceClosureType type = info._type;
const char* type_name = MetaspaceObj::type_name(type); const char* type_name = MetaspaceClosure::type_name(type);
log_as_hex(last_obj_base, buffered_addr, last_obj_base + _buffer_to_requested_delta); log_as_hex(last_obj_base, buffered_addr, last_obj_base + _buffer_to_requested_delta);
switch (type) { switch (type) {
case MetaspaceObj::ClassType: case MetaspaceClosureType::ClassType:
log_klass((Klass*)src, requested_addr, type_name, bytes, current); log_klass((Klass*)src, requested_addr, type_name, bytes, current);
break; break;
case MetaspaceObj::ConstantPoolType: case MetaspaceClosureType::ConstantPoolType:
log_constant_pool((ConstantPool*)src, requested_addr, type_name, bytes, current); log_constant_pool((ConstantPool*)src, requested_addr, type_name, bytes, current);
break; break;
case MetaspaceObj::ConstantPoolCacheType: case MetaspaceClosureType::ConstantPoolCacheType:
log_constant_pool_cache((ConstantPoolCache*)src, requested_addr, type_name, bytes, current); log_constant_pool_cache((ConstantPoolCache*)src, requested_addr, type_name, bytes, current);
break; break;
case MetaspaceObj::ConstMethodType: case MetaspaceClosureType::ConstMethodType:
log_const_method((ConstMethod*)src, requested_addr, type_name, bytes, current); log_const_method((ConstMethod*)src, requested_addr, type_name, bytes, current);
break; break;
case MetaspaceObj::MethodType: case MetaspaceClosureType::MethodType:
log_method((Method*)src, requested_addr, type_name, bytes, current); log_method((Method*)src, requested_addr, type_name, bytes, current);
break; break;
case MetaspaceObj::MethodCountersType: case MetaspaceClosureType::MethodCountersType:
log_method_counters((MethodCounters*)src, requested_addr, type_name, bytes, current); log_method_counters((MethodCounters*)src, requested_addr, type_name, bytes, current);
break; break;
case MetaspaceObj::MethodDataType: case MetaspaceClosureType::MethodDataType:
log_method_data((MethodData*)src, requested_addr, type_name, bytes, current); log_method_data((MethodData*)src, requested_addr, type_name, bytes, current);
break; break;
case MetaspaceObj::SymbolType: case MetaspaceClosureType::ModuleEntryType:
log_module_entry((ModuleEntry*)src, requested_addr, type_name, bytes, current);
break;
case MetaspaceClosureType::PackageEntryType:
log_package_entry((PackageEntry*)src, requested_addr, type_name, bytes, current);
break;
case MetaspaceClosureType::GrowableArrayType:
log_growable_array((GrowableArrayBase*)src, requested_addr, type_name, bytes, current);
break;
case MetaspaceClosureType::SymbolType:
log_symbol((Symbol*)src, requested_addr, type_name, bytes, current); log_symbol((Symbol*)src, requested_addr, type_name, bytes, current);
break; break;
case MetaspaceObj::KlassTrainingDataType: case MetaspaceClosureType::KlassTrainingDataType:
log_klass_training_data((KlassTrainingData*)src, requested_addr, type_name, bytes, current); log_klass_training_data((KlassTrainingData*)src, requested_addr, type_name, bytes, current);
break; break;
case MetaspaceObj::MethodTrainingDataType: case MetaspaceClosureType::MethodTrainingDataType:
log_method_training_data((MethodTrainingData*)src, requested_addr, type_name, bytes, current); log_method_training_data((MethodTrainingData*)src, requested_addr, type_name, bytes, current);
break; break;
case MetaspaceObj::CompileTrainingDataType: case MetaspaceClosureType::CompileTrainingDataType:
log_compile_training_data((CompileTrainingData*)src, requested_addr, type_name, bytes, current); log_compile_training_data((CompileTrainingData*)src, requested_addr, type_name, bytes, current);
break; break;
default: default:
@ -421,6 +432,27 @@ void AOTMapLogger::log_method_data(MethodData* md, address requested_addr, const
log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, md->method()->external_name()); log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes, md->method()->external_name());
} }
void AOTMapLogger::log_module_entry(ModuleEntry* mod, address requested_addr, const char* type_name,
int bytes, Thread* current) {
ResourceMark rm(current);
log_debug(aot, map)(_LOG_PREFIX " %s", p2i(requested_addr), type_name, bytes,
mod->name_as_C_string());
}
void AOTMapLogger::log_package_entry(PackageEntry* pkg, address requested_addr, const char* type_name,
int bytes, Thread* current) {
ResourceMark rm(current);
log_debug(aot, map)(_LOG_PREFIX " %s - %s", p2i(requested_addr), type_name, bytes,
pkg->module()->name_as_C_string(), pkg->name_as_C_string());
}
void AOTMapLogger::log_growable_array(GrowableArrayBase* arr, address requested_addr, const char* type_name,
int bytes, Thread* current) {
ResourceMark rm(current);
log_debug(aot, map)(_LOG_PREFIX " %d (%d)", p2i(requested_addr), type_name, bytes,
arr->length(), arr->capacity());
}
void AOTMapLogger::log_klass(Klass* k, address requested_addr, const char* type_name, void AOTMapLogger::log_klass(Klass* k, address requested_addr, const char* type_name,
int bytes, Thread* current) { int bytes, Thread* current) {
ResourceMark rm(current); ResourceMark rm(current);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2025, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -28,6 +28,7 @@
#include "cds/archiveBuilder.hpp" #include "cds/archiveBuilder.hpp"
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "memory/allStatic.hpp" #include "memory/allStatic.hpp"
#include "memory/metaspaceClosureType.hpp"
#include "oops/oopsHierarchy.hpp" #include "oops/oopsHierarchy.hpp"
#include "utilities/globalDefinitions.hpp" #include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp" #include "utilities/growableArray.hpp"
@ -37,9 +38,13 @@ class ArchiveStreamedHeapInfo;
class CompileTrainingData; class CompileTrainingData;
class DumpRegion; class DumpRegion;
class FileMapInfo; class FileMapInfo;
class GrowableArrayBase;
class KlassTrainingData; class KlassTrainingData;
class MethodCounters;
class MethodTrainingData; class MethodTrainingData;
class ModuleEntry;
class outputStream; class outputStream;
class PackageEntry;
// Write detailed info to a mapfile to analyze contents of the AOT cache/CDS archive. // Write detailed info to a mapfile to analyze contents of the AOT cache/CDS archive.
// -Xlog:aot+map* can be used both when creating an AOT cache, or when using an AOT cache. // -Xlog:aot+map* can be used both when creating an AOT cache, or when using an AOT cache.
@ -62,7 +67,7 @@ class AOTMapLogger : AllStatic {
address _buffered_addr; address _buffered_addr;
address _requested_addr; address _requested_addr;
int _bytes; int _bytes;
MetaspaceObj::Type _type; MetaspaceClosureType _type;
}; };
public: public:
@ -142,6 +147,9 @@ private:
Thread* current); Thread* current);
static void log_klass(Klass* k, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_klass(Klass* k, address requested_addr, const char* type_name, int bytes, Thread* current);
static void log_method(Method* m, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_method(Method* m, address requested_addr, const char* type_name, int bytes, Thread* current);
static void log_module_entry(ModuleEntry* mod, address requested_addr, const char* type_name, int bytes, Thread* current);
static void log_package_entry(PackageEntry* pkg, address requested_addr, const char* type_name, int bytes, Thread* current);
static void log_growable_array(GrowableArrayBase* arr, address requested_addr, const char* type_name, int bytes, Thread* current);
static void log_symbol(Symbol* s, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_symbol(Symbol* s, address requested_addr, const char* type_name, int bytes, Thread* current);
static void log_klass_training_data(KlassTrainingData* ktd, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_klass_training_data(KlassTrainingData* ktd, address requested_addr, const char* type_name, int bytes, Thread* current);
static void log_method_training_data(MethodTrainingData* mtd, address requested_addr, const char* type_name, int bytes, Thread* current); static void log_method_training_data(MethodTrainingData* mtd, address requested_addr, const char* type_name, int bytes, Thread* current);

View File

@ -698,6 +698,9 @@ public:
Universe::metaspace_pointers_do(it); Universe::metaspace_pointers_do(it);
vmSymbols::metaspace_pointers_do(it); vmSymbols::metaspace_pointers_do(it);
TrainingData::iterate_roots(it); TrainingData::iterate_roots(it);
if (CDSConfig::is_dumping_full_module_graph()) {
ClassLoaderDataShared::iterate_roots(it);
}
// The above code should find all the symbols that are referenced by the // The above code should find all the symbols that are referenced by the
// archived classes. We just need to add the extra symbols which // archived classes. We just need to add the extra symbols which
@ -795,6 +798,10 @@ void VM_PopulateDumpSharedSpace::doit() {
_builder.make_klasses_shareable(); _builder.make_klasses_shareable();
AOTMetaspace::make_method_handle_intrinsics_shareable(); AOTMetaspace::make_method_handle_intrinsics_shareable();
if (CDSConfig::is_dumping_full_module_graph()) {
ClassLoaderDataShared::remove_unshareable_info();
}
dump_java_heap_objects(); dump_java_heap_objects();
dump_shared_symbol_table(_builder.symbols()); dump_shared_symbol_table(_builder.symbols());
@ -1135,6 +1142,7 @@ void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS
HeapShared::init_heap_writer(); HeapShared::init_heap_writer();
if (CDSConfig::is_dumping_full_module_graph()) { if (CDSConfig::is_dumping_full_module_graph()) {
ClassLoaderDataShared::ensure_module_entry_tables_exist(); ClassLoaderDataShared::ensure_module_entry_tables_exist();
ClassLoaderDataShared::build_tables(CHECK);
HeapShared::reset_archived_object_states(CHECK); HeapShared::reset_archived_object_states(CHECK);
} }

View File

@ -243,7 +243,7 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re
if (get_follow_mode(ref) != make_a_copy) { if (get_follow_mode(ref) != make_a_copy) {
return false; return false;
} }
if (ref->msotype() == MetaspaceObj::ClassType) { if (ref->type() == MetaspaceClosureType::ClassType) {
Klass* klass = (Klass*)ref->obj(); Klass* klass = (Klass*)ref->obj();
assert(klass->is_klass(), "must be"); assert(klass->is_klass(), "must be");
if (!is_excluded(klass)) { if (!is_excluded(klass)) {
@ -252,7 +252,7 @@ bool ArchiveBuilder::gather_klass_and_symbol(MetaspaceClosure::Ref* ref, bool re
assert(klass->is_instance_klass(), "must be"); assert(klass->is_instance_klass(), "must be");
} }
} }
} else if (ref->msotype() == MetaspaceObj::SymbolType) { } else if (ref->type() == MetaspaceClosureType::SymbolType) {
// Make sure the symbol won't be GC'ed while we are dumping the archive. // Make sure the symbol won't be GC'ed while we are dumping the archive.
Symbol* sym = (Symbol*)ref->obj(); Symbol* sym = (Symbol*)ref->obj();
sym->increment_refcount(); sym->increment_refcount();
@ -271,11 +271,6 @@ void ArchiveBuilder::gather_klasses_and_symbols() {
aot_log_info(aot)("Gathering classes and symbols ... "); aot_log_info(aot)("Gathering classes and symbols ... ");
GatherKlassesAndSymbols doit(this); GatherKlassesAndSymbols doit(this);
iterate_roots(&doit); iterate_roots(&doit);
#if INCLUDE_CDS_JAVA_HEAP
if (CDSConfig::is_dumping_full_module_graph()) {
ClassLoaderDataShared::iterate_symbols(&doit);
}
#endif
doit.finish(); doit.finish();
if (CDSConfig::is_dumping_static_archive()) { if (CDSConfig::is_dumping_static_archive()) {
@ -446,14 +441,14 @@ bool ArchiveBuilder::gather_one_source_obj(MetaspaceClosure::Ref* ref, bool read
} }
#ifdef ASSERT #ifdef ASSERT
if (ref->msotype() == MetaspaceObj::MethodType) { if (ref->type() == MetaspaceClosureType::MethodType) {
Method* m = (Method*)ref->obj(); Method* m = (Method*)ref->obj();
assert(!RegeneratedClasses::has_been_regenerated((address)m->method_holder()), assert(!RegeneratedClasses::has_been_regenerated((address)m->method_holder()),
"Should not archive methods in a class that has been regenerated"); "Should not archive methods in a class that has been regenerated");
} }
#endif #endif
if (ref->msotype() == MetaspaceObj::MethodDataType) { if (ref->type() == MetaspaceClosureType::MethodDataType) {
MethodData* md = (MethodData*)ref->obj(); MethodData* md = (MethodData*)ref->obj();
md->clean_method_data(false /* always_clean */); md->clean_method_data(false /* always_clean */);
} }
@ -554,16 +549,16 @@ ArchiveBuilder::FollowMode ArchiveBuilder::get_follow_mode(MetaspaceClosure::Ref
if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(obj)) { if (CDSConfig::is_dumping_dynamic_archive() && AOTMetaspace::in_aot_cache(obj)) {
// Don't dump existing shared metadata again. // Don't dump existing shared metadata again.
return point_to_it; return point_to_it;
} else if (ref->msotype() == MetaspaceObj::MethodDataType || } else if (ref->type() == MetaspaceClosureType::MethodDataType ||
ref->msotype() == MetaspaceObj::MethodCountersType || ref->type() == MetaspaceClosureType::MethodCountersType ||
ref->msotype() == MetaspaceObj::KlassTrainingDataType || ref->type() == MetaspaceClosureType::KlassTrainingDataType ||
ref->msotype() == MetaspaceObj::MethodTrainingDataType || ref->type() == MetaspaceClosureType::MethodTrainingDataType ||
ref->msotype() == MetaspaceObj::CompileTrainingDataType) { ref->type() == MetaspaceClosureType::CompileTrainingDataType) {
return (TrainingData::need_data() || TrainingData::assembling_data()) ? make_a_copy : set_to_null; return (TrainingData::need_data() || TrainingData::assembling_data()) ? make_a_copy : set_to_null;
} else if (ref->msotype() == MetaspaceObj::AdapterHandlerEntryType) { } else if (ref->type() == MetaspaceClosureType::AdapterHandlerEntryType) {
return CDSConfig::is_dumping_adapters() ? make_a_copy : set_to_null; return CDSConfig::is_dumping_adapters() ? make_a_copy : set_to_null;
} else { } else {
if (ref->msotype() == MetaspaceObj::ClassType) { if (ref->type() == MetaspaceClosureType::ClassType) {
Klass* klass = (Klass*)ref->obj(); Klass* klass = (Klass*)ref->obj();
assert(klass->is_klass(), "must be"); assert(klass->is_klass(), "must be");
if (RegeneratedClasses::has_been_regenerated(klass)) { if (RegeneratedClasses::has_been_regenerated(klass)) {
@ -620,15 +615,6 @@ void ArchiveBuilder::dump_rw_metadata() {
ResourceMark rm; ResourceMark rm;
aot_log_info(aot)("Allocating RW objects ... "); aot_log_info(aot)("Allocating RW objects ... ");
make_shallow_copies(&_rw_region, &_rw_src_objs); make_shallow_copies(&_rw_region, &_rw_src_objs);
#if INCLUDE_CDS_JAVA_HEAP
if (CDSConfig::is_dumping_full_module_graph()) {
// Archive the ModuleEntry's and PackageEntry's of the 3 built-in loaders
char* start = rw_region()->top();
ClassLoaderDataShared::allocate_archived_tables();
alloc_stats()->record_modules(rw_region()->top() - start, /*read_only*/false);
}
#endif
} }
void ArchiveBuilder::dump_ro_metadata() { void ArchiveBuilder::dump_ro_metadata() {
@ -637,15 +623,6 @@ void ArchiveBuilder::dump_ro_metadata() {
start_dump_region(&_ro_region); start_dump_region(&_ro_region);
make_shallow_copies(&_ro_region, &_ro_src_objs); make_shallow_copies(&_ro_region, &_ro_src_objs);
#if INCLUDE_CDS_JAVA_HEAP
if (CDSConfig::is_dumping_full_module_graph()) {
char* start = ro_region()->top();
ClassLoaderDataShared::init_archived_tables();
alloc_stats()->record_modules(ro_region()->top() - start, /*read_only*/true);
}
#endif
RegeneratedClasses::record_regenerated_objects(); RegeneratedClasses::record_regenerated_objects();
} }
@ -663,7 +640,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s
size_t alignment = SharedSpaceObjectAlignment; // alignment for the dest pointer size_t alignment = SharedSpaceObjectAlignment; // alignment for the dest pointer
char* oldtop = dump_region->top(); char* oldtop = dump_region->top();
if (src_info->msotype() == MetaspaceObj::ClassType) { if (src_info->type() == MetaspaceClosureType::ClassType) {
// Allocate space for a pointer directly in front of the future InstanceKlass, so // Allocate space for a pointer directly in front of the future InstanceKlass, so
// we can do a quick lookup from InstanceKlass* -> RunTimeClassInfo* // we can do a quick lookup from InstanceKlass* -> RunTimeClassInfo*
// without building another hashtable. See RunTimeClassInfo::get_for() // without building another hashtable. See RunTimeClassInfo::get_for()
@ -679,7 +656,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s
alignment = nth_bit(ArchiveBuilder::precomputed_narrow_klass_shift()); alignment = nth_bit(ArchiveBuilder::precomputed_narrow_klass_shift());
} }
#endif #endif
} else if (src_info->msotype() == MetaspaceObj::SymbolType) { } else if (src_info->type() == MetaspaceClosureType::SymbolType) {
// Symbols may be allocated by using AllocateHeap, so their sizes // Symbols may be allocated by using AllocateHeap, so their sizes
// may be less than size_in_bytes() indicates. // may be less than size_in_bytes() indicates.
bytes = ((Symbol*)src)->byte_size(); bytes = ((Symbol*)src)->byte_size();
@ -689,7 +666,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s
memcpy(dest, src, bytes); memcpy(dest, src, bytes);
// Update the hash of buffered sorted symbols for static dump so that the symbols have deterministic contents // Update the hash of buffered sorted symbols for static dump so that the symbols have deterministic contents
if (CDSConfig::is_dumping_static_archive() && (src_info->msotype() == MetaspaceObj::SymbolType)) { if (CDSConfig::is_dumping_static_archive() && (src_info->type() == MetaspaceClosureType::SymbolType)) {
Symbol* buffered_symbol = (Symbol*)dest; Symbol* buffered_symbol = (Symbol*)dest;
assert(((Symbol*)src)->is_permanent(), "archived symbols must be permanent"); assert(((Symbol*)src)->is_permanent(), "archived symbols must be permanent");
buffered_symbol->update_identity_hash(); buffered_symbol->update_identity_hash();
@ -704,7 +681,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s
} }
} }
intptr_t* archived_vtable = CppVtables::get_archived_vtable(src_info->msotype(), (address)dest); intptr_t* archived_vtable = CppVtables::get_archived_vtable(src_info->type(), (address)dest);
if (archived_vtable != nullptr) { if (archived_vtable != nullptr) {
*(address*)dest = (address)archived_vtable; *(address*)dest = (address)archived_vtable;
ArchivePtrMarker::mark_pointer((address*)dest); ArchivePtrMarker::mark_pointer((address*)dest);
@ -714,7 +691,7 @@ void ArchiveBuilder::make_shallow_copy(DumpRegion *dump_region, SourceObjInfo* s
src_info->set_buffered_addr((address)dest); src_info->set_buffered_addr((address)dest);
char* newtop = dump_region->top(); char* newtop = dump_region->top();
_alloc_stats.record(src_info->msotype(), int(newtop - oldtop), src_info->read_only()); _alloc_stats.record(src_info->type(), int(newtop - oldtop), src_info->read_only());
DEBUG_ONLY(_alloc_stats.verify((int)dump_region->used(), src_info->read_only())); DEBUG_ONLY(_alloc_stats.verify((int)dump_region->used(), src_info->read_only()));
} }
@ -997,15 +974,15 @@ void ArchiveBuilder::make_training_data_shareable() {
return; return;
} }
if (info.msotype() == MetaspaceObj::KlassTrainingDataType || if (info.type() == MetaspaceClosureType::KlassTrainingDataType ||
info.msotype() == MetaspaceObj::MethodTrainingDataType || info.type() == MetaspaceClosureType::MethodTrainingDataType ||
info.msotype() == MetaspaceObj::CompileTrainingDataType) { info.type() == MetaspaceClosureType::CompileTrainingDataType) {
TrainingData* buffered_td = (TrainingData*)info.buffered_addr(); TrainingData* buffered_td = (TrainingData*)info.buffered_addr();
buffered_td->remove_unshareable_info(); buffered_td->remove_unshareable_info();
} else if (info.msotype() == MetaspaceObj::MethodDataType) { } else if (info.type() == MetaspaceClosureType::MethodDataType) {
MethodData* buffered_mdo = (MethodData*)info.buffered_addr(); MethodData* buffered_mdo = (MethodData*)info.buffered_addr();
buffered_mdo->remove_unshareable_info(); buffered_mdo->remove_unshareable_info();
} else if (info.msotype() == MetaspaceObj::MethodCountersType) { } else if (info.type() == MetaspaceClosureType::MethodCountersType) {
MethodCounters* buffered_mc = (MethodCounters*)info.buffered_addr(); MethodCounters* buffered_mc = (MethodCounters*)info.buffered_addr();
buffered_mc->remove_unshareable_info(); buffered_mc->remove_unshareable_info();
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -134,13 +134,13 @@ private:
int _size_in_bytes; int _size_in_bytes;
int _id; // Each object has a unique serial ID, starting from zero. The ID is assigned int _id; // Each object has a unique serial ID, starting from zero. The ID is assigned
// when the object is added into _source_objs. // when the object is added into _source_objs.
MetaspaceObj::Type _msotype; MetaspaceClosureType _type;
address _source_addr; // The source object to be copied. address _source_addr; // The source object to be copied.
address _buffered_addr; // The copy of this object insider the buffer. address _buffered_addr; // The copy of this object insider the buffer.
public: public:
SourceObjInfo(MetaspaceClosure::Ref* ref, bool read_only, FollowMode follow_mode) : SourceObjInfo(MetaspaceClosure::Ref* ref, bool read_only, FollowMode follow_mode) :
_ptrmap_start(0), _ptrmap_end(0), _read_only(read_only), _has_embedded_pointer(false), _follow_mode(follow_mode), _ptrmap_start(0), _ptrmap_end(0), _read_only(read_only), _has_embedded_pointer(false), _follow_mode(follow_mode),
_size_in_bytes(ref->size() * BytesPerWord), _id(0), _msotype(ref->msotype()), _size_in_bytes(ref->size() * BytesPerWord), _id(0), _type(ref->type()),
_source_addr(ref->obj()) { _source_addr(ref->obj()) {
if (follow_mode == point_to_it) { if (follow_mode == point_to_it) {
_buffered_addr = ref->obj(); _buffered_addr = ref->obj();
@ -155,7 +155,7 @@ private:
SourceObjInfo(address src, SourceObjInfo* renegerated_obj_info) : SourceObjInfo(address src, SourceObjInfo* renegerated_obj_info) :
_ptrmap_start(0), _ptrmap_end(0), _read_only(false), _ptrmap_start(0), _ptrmap_end(0), _read_only(false),
_follow_mode(renegerated_obj_info->_follow_mode), _follow_mode(renegerated_obj_info->_follow_mode),
_size_in_bytes(0), _msotype(renegerated_obj_info->_msotype), _size_in_bytes(0), _type(renegerated_obj_info->_type),
_source_addr(src), _buffered_addr(renegerated_obj_info->_buffered_addr) {} _source_addr(src), _buffered_addr(renegerated_obj_info->_buffered_addr) {}
bool should_copy() const { return _follow_mode == make_a_copy; } bool should_copy() const { return _follow_mode == make_a_copy; }
@ -182,7 +182,7 @@ private:
} }
return _buffered_addr; return _buffered_addr;
} }
MetaspaceObj::Type msotype() const { return _msotype; } MetaspaceClosureType type() const { return _type; }
FollowMode follow_mode() const { return _follow_mode; } FollowMode follow_mode() const { return _follow_mode; }
}; };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -22,12 +22,14 @@
* *
*/ */
#include "cds/aotGrowableArray.hpp"
#include "cds/aotMetaspace.hpp" #include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp" #include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp" #include "cds/archiveUtils.hpp"
#include "cds/cdsConfig.hpp" #include "cds/cdsConfig.hpp"
#include "cds/cppVtables.hpp" #include "cds/cppVtables.hpp"
#include "logging/log.hpp" #include "logging/log.hpp"
#include "memory/resourceArea.hpp"
#include "oops/instanceClassLoaderKlass.hpp" #include "oops/instanceClassLoaderKlass.hpp"
#include "oops/instanceMirrorKlass.hpp" #include "oops/instanceMirrorKlass.hpp"
#include "oops/instanceRefKlass.hpp" #include "oops/instanceRefKlass.hpp"
@ -53,6 +55,19 @@
// + at run time: we clone the actual contents of the vtables from libjvm.so // + at run time: we clone the actual contents of the vtables from libjvm.so
// into our own tables. // into our own tables.
#ifndef PRODUCT
// AOTGrowableArray has a vtable only when in non-product builds (due to
// the virtual printing functions in AnyObj).
using GrowableArray_ModuleEntry_ptr = AOTGrowableArray<ModuleEntry*>;
#define DEBUG_CPP_VTABLE_TYPES_DO(f) \
f(GrowableArray_ModuleEntry_ptr) \
#endif
// Currently, the archive contains ONLY the following types of objects that have C++ vtables. // Currently, the archive contains ONLY the following types of objects that have C++ vtables.
#define CPP_VTABLE_TYPES_DO(f) \ #define CPP_VTABLE_TYPES_DO(f) \
f(ConstantPool) \ f(ConstantPool) \
@ -68,7 +83,8 @@
f(TypeArrayKlass) \ f(TypeArrayKlass) \
f(KlassTrainingData) \ f(KlassTrainingData) \
f(MethodTrainingData) \ f(MethodTrainingData) \
f(CompileTrainingData) f(CompileTrainingData) \
NOT_PRODUCT(DEBUG_CPP_VTABLE_TYPES_DO(f))
class CppVtableInfo { class CppVtableInfo {
intptr_t _vtable_size; intptr_t _vtable_size;
@ -86,7 +102,7 @@ public:
} }
}; };
static inline intptr_t* vtable_of(const Metadata* m) { static inline intptr_t* vtable_of(const void* m) {
return *((intptr_t**)m); return *((intptr_t**)m);
} }
@ -116,6 +132,7 @@ CppVtableInfo* CppVtableCloner<T>::allocate_and_initialize(const char* name) {
template <class T> template <class T>
void CppVtableCloner<T>::initialize(const char* name, CppVtableInfo* info) { void CppVtableCloner<T>::initialize(const char* name, CppVtableInfo* info) {
ResourceMark rm;
T tmp; // Allocate temporary dummy metadata object to get to the original vtable. T tmp; // Allocate temporary dummy metadata object to get to the original vtable.
int n = info->vtable_size(); int n = info->vtable_size();
intptr_t* srcvtable = vtable_of(&tmp); intptr_t* srcvtable = vtable_of(&tmp);
@ -268,7 +285,7 @@ void CppVtables::serialize(SerializeClosure* soc) {
} }
} }
intptr_t* CppVtables::get_archived_vtable(MetaspaceObj::Type msotype, address obj) { intptr_t* CppVtables::get_archived_vtable(MetaspaceClosureType type, address obj) {
if (!_orig_cpp_vtptrs_inited) { if (!_orig_cpp_vtptrs_inited) {
CPP_VTABLE_TYPES_DO(INIT_ORIG_CPP_VTPTRS); CPP_VTABLE_TYPES_DO(INIT_ORIG_CPP_VTPTRS);
_orig_cpp_vtptrs_inited = true; _orig_cpp_vtptrs_inited = true;
@ -276,19 +293,23 @@ intptr_t* CppVtables::get_archived_vtable(MetaspaceObj::Type msotype, address ob
assert(CDSConfig::is_dumping_archive(), "sanity"); assert(CDSConfig::is_dumping_archive(), "sanity");
int kind = -1; int kind = -1;
switch (msotype) { switch (type) {
case MetaspaceObj::SymbolType: case MetaspaceClosureType::SymbolType:
case MetaspaceObj::TypeArrayU1Type: case MetaspaceClosureType::TypeArrayU1Type:
case MetaspaceObj::TypeArrayU2Type: case MetaspaceClosureType::TypeArrayU2Type:
case MetaspaceObj::TypeArrayU4Type: case MetaspaceClosureType::TypeArrayU4Type:
case MetaspaceObj::TypeArrayU8Type: case MetaspaceClosureType::TypeArrayU8Type:
case MetaspaceObj::TypeArrayOtherType: case MetaspaceClosureType::TypeArrayOtherType:
case MetaspaceObj::ConstMethodType: case MetaspaceClosureType::CArrayType:
case MetaspaceObj::ConstantPoolCacheType: case MetaspaceClosureType::ConstMethodType:
case MetaspaceObj::AnnotationsType: case MetaspaceClosureType::ConstantPoolCacheType:
case MetaspaceObj::RecordComponentType: case MetaspaceClosureType::AnnotationsType:
case MetaspaceObj::AdapterHandlerEntryType: case MetaspaceClosureType::ModuleEntryType:
case MetaspaceObj::AdapterFingerPrintType: case MetaspaceClosureType::PackageEntryType:
case MetaspaceClosureType::RecordComponentType:
case MetaspaceClosureType::AdapterHandlerEntryType:
case MetaspaceClosureType::AdapterFingerPrintType:
PRODUCT_ONLY(case MetaspaceClosureType::GrowableArrayType:)
// These have no vtables. // These have no vtables.
break; break;
default: default:

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -27,6 +27,7 @@
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "memory/allStatic.hpp" #include "memory/allStatic.hpp"
#include "memory/metaspaceClosureType.hpp"
#include "utilities/globalDefinitions.hpp" #include "utilities/globalDefinitions.hpp"
class ArchiveBuilder; class ArchiveBuilder;
@ -40,7 +41,7 @@ class CppVtables : AllStatic {
public: public:
static void dumptime_init(ArchiveBuilder* builder); static void dumptime_init(ArchiveBuilder* builder);
static void zero_archived_vtables(); static void zero_archived_vtables();
static intptr_t* get_archived_vtable(MetaspaceObj::Type msotype, address obj); static intptr_t* get_archived_vtable(MetaspaceClosureType type, address obj);
static void serialize(SerializeClosure* sc); static void serialize(SerializeClosure* sc);
static bool is_valid_shared_method(const Method* m) NOT_CDS_RETURN_(false); static bool is_valid_shared_method(const Method* m) NOT_CDS_RETURN_(false);
static char* vtables_serialized_base() { return _vtables_serialized_base; } static char* vtables_serialized_base() { return _vtables_serialized_base; }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -27,32 +27,34 @@
#include "classfile/compactHashtable.hpp" #include "classfile/compactHashtable.hpp"
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "memory/metaspaceClosureType.hpp"
// This is for dumping detailed statistics for the allocations // This is for dumping detailed statistics for the allocations
// in the shared spaces. // in the shared spaces.
class DumpAllocStats : public StackObj { class DumpAllocStats : public StackObj {
public: public:
// Here's poor man's enum inheritance #define DUMPED_OBJ_TYPES_DO(f) \
#define SHAREDSPACE_OBJ_TYPES_DO(f) \ METASPACE_CLOSURE_TYPES_DO(f) \
METASPACE_OBJ_TYPES_DO(f) \
f(SymbolHashentry) \ f(SymbolHashentry) \
f(SymbolBucket) \ f(SymbolBucket) \
f(StringHashentry) \ f(StringHashentry) \
f(StringBucket) \ f(StringBucket) \
f(ModulesNatives) \
f(CppVTables) \ f(CppVTables) \
f(Other) f(Other)
#define DUMPED_TYPE_DECLARE(name) name ## Type,
#define DUMPED_TYPE_NAME_CASE(name) case name ## Type: return #name;
enum Type { enum Type {
// Types are MetaspaceObj::ClassType, MetaspaceObj::SymbolType, etc // Types are MetaspaceObj::ClassType, MetaspaceObj::SymbolType, etc
SHAREDSPACE_OBJ_TYPES_DO(METASPACE_OBJ_TYPE_DECLARE) DUMPED_OBJ_TYPES_DO(DUMPED_TYPE_DECLARE)
_number_of_types _number_of_types
}; };
static const char* type_name(Type type) { static const char* type_name(Type type) {
switch(type) { switch(type) {
SHAREDSPACE_OBJ_TYPES_DO(METASPACE_OBJ_TYPE_NAME_CASE) DUMPED_OBJ_TYPES_DO(DUMPED_TYPE_NAME_CASE)
default: default:
ShouldNotReachHere(); ShouldNotReachHere();
return nullptr; return nullptr;
@ -101,16 +103,12 @@ public:
CompactHashtableStats* symbol_stats() { return &_symbol_stats; } CompactHashtableStats* symbol_stats() { return &_symbol_stats; }
CompactHashtableStats* string_stats() { return &_string_stats; } CompactHashtableStats* string_stats() { return &_string_stats; }
void record(MetaspaceObj::Type type, int byte_size, bool read_only) { void record(MetaspaceClosureType type, int byte_size, bool read_only) {
assert(int(type) >= 0 && type < MetaspaceObj::_number_of_types, "sanity"); int t = (int)type;
assert(t >= 0 && t < (int)MetaspaceClosureType::_number_of_types, "sanity");
int which = (read_only) ? RO : RW; int which = (read_only) ? RO : RW;
_counts[which][type] ++; _counts[which][t] ++;
_bytes [which][type] += byte_size; _bytes [which][t] += byte_size;
}
void record_modules(int byte_size, bool read_only) {
int which = (read_only) ? RO : RW;
_bytes [which][ModulesNativesType] += byte_size;
} }
void record_other_type(int byte_size, bool read_only) { void record_other_type(int byte_size, bool read_only) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2018, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -948,10 +948,6 @@ void HeapShared::archive_subgraphs() {
true /* is_full_module_graph */); true /* is_full_module_graph */);
} }
} }
if (CDSConfig::is_dumping_full_module_graph()) {
Modules::verify_archived_modules();
}
} }
// //

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -33,6 +33,7 @@
#include "classfile/packageEntry.hpp" #include "classfile/packageEntry.hpp"
#include "classfile/systemDictionary.hpp" #include "classfile/systemDictionary.hpp"
#include "logging/log.hpp" #include "logging/log.hpp"
#include "memory/metaspaceClosure.hpp"
#include "runtime/handles.inline.hpp" #include "runtime/handles.inline.hpp"
#include "runtime/safepoint.hpp" #include "runtime/safepoint.hpp"
@ -56,9 +57,9 @@ class ArchivedClassLoaderData {
public: public:
ArchivedClassLoaderData() : _packages(nullptr), _modules(nullptr), _unnamed_module(nullptr) {} ArchivedClassLoaderData() : _packages(nullptr), _modules(nullptr), _unnamed_module(nullptr) {}
void iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure); void iterate_roots(MetaspaceClosure* closure);
void allocate(ClassLoaderData* loader_data); void build_tables(ClassLoaderData* loader_data, TRAPS);
void init_archived_entries(ClassLoaderData* loader_data); void remove_unshareable_info();
ModuleEntry* unnamed_module() { ModuleEntry* unnamed_module() {
return _unnamed_module; return _unnamed_module;
} }
@ -80,17 +81,14 @@ static ModuleEntry* _archived_javabase_moduleEntry = nullptr;
static int _platform_loader_root_index = -1; static int _platform_loader_root_index = -1;
static int _system_loader_root_index = -1; static int _system_loader_root_index = -1;
void ArchivedClassLoaderData::iterate_symbols(ClassLoaderData* loader_data, MetaspaceClosure* closure) { void ArchivedClassLoaderData::iterate_roots(MetaspaceClosure* it) {
assert(CDSConfig::is_dumping_full_module_graph(), "must be"); assert(CDSConfig::is_dumping_full_module_graph(), "must be");
assert_valid(loader_data); it->push(&_packages);
if (loader_data != nullptr) { it->push(&_modules);
loader_data->packages()->iterate_symbols(closure); it->push(&_unnamed_module);
loader_data->modules() ->iterate_symbols(closure);
loader_data->unnamed_module()->iterate_symbols(closure);
}
} }
void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) { void ArchivedClassLoaderData::build_tables(ClassLoaderData* loader_data, TRAPS) {
assert(CDSConfig::is_dumping_full_module_graph(), "must be"); assert(CDSConfig::is_dumping_full_module_graph(), "must be");
assert_valid(loader_data); assert_valid(loader_data);
if (loader_data != nullptr) { if (loader_data != nullptr) {
@ -98,19 +96,28 @@ void ArchivedClassLoaderData::allocate(ClassLoaderData* loader_data) {
// address of the Symbols, which may be relocated at runtime due to ASLR. // address of the Symbols, which may be relocated at runtime due to ASLR.
// So we store the packages/modules in Arrays. At runtime, we create // So we store the packages/modules in Arrays. At runtime, we create
// the hashtables using these arrays. // the hashtables using these arrays.
_packages = loader_data->packages()->allocate_archived_entries(); _packages = loader_data->packages()->build_aot_table(loader_data, CHECK);
_modules = loader_data->modules() ->allocate_archived_entries(); _modules = loader_data->modules()->build_aot_table(loader_data, CHECK);
_unnamed_module = loader_data->unnamed_module()->allocate_archived_entry(); _unnamed_module = loader_data->unnamed_module();
} }
} }
void ArchivedClassLoaderData::init_archived_entries(ClassLoaderData* loader_data) { void ArchivedClassLoaderData::remove_unshareable_info() {
assert(CDSConfig::is_dumping_full_module_graph(), "must be"); if (_packages != nullptr) {
assert_valid(loader_data); _packages = ArchiveBuilder::current()->get_buffered_addr(_packages);
if (loader_data != nullptr) { for (int i = 0; i < _packages->length(); i++) {
loader_data->packages()->init_archived_entries(_packages); _packages->at(i)->remove_unshareable_info();
loader_data->modules() ->init_archived_entries(_modules); }
_unnamed_module->init_as_archived_entry(); }
if (_modules != nullptr) {
_modules = ArchiveBuilder::current()->get_buffered_addr(_modules);
for (int i = 0; i < _modules->length(); i++) {
_modules->at(i)->remove_unshareable_info();
}
}
if (_unnamed_module != nullptr) {
_unnamed_module = ArchiveBuilder::current()->get_buffered_addr(_unnamed_module);
_unnamed_module->remove_unshareable_info();
} }
} }
@ -153,7 +160,6 @@ void ArchivedClassLoaderData::clear_archived_oops() {
// ------------------------------ // ------------------------------
void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() { void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() {
#if INCLUDE_CDS_JAVA_HEAP
// The streaming object loader prefers loading the class loader related objects before // The streaming object loader prefers loading the class loader related objects before
// the CLD constructor which has a NoSafepointVerifier. // the CLD constructor which has a NoSafepointVerifier.
if (!HeapShared::is_loading_streaming_mode()) { if (!HeapShared::is_loading_streaming_mode()) {
@ -178,7 +184,6 @@ void ClassLoaderDataShared::load_archived_platform_and_system_class_loaders() {
if (system_loader_module_entry != nullptr) { if (system_loader_module_entry != nullptr) {
system_loader_module_entry->preload_archived_oops(); system_loader_module_entry->preload_archived_oops();
} }
#endif
} }
static ClassLoaderData* null_class_loader_data() { static ClassLoaderData* null_class_loader_data() {
@ -210,28 +215,27 @@ void ClassLoaderDataShared::ensure_module_entry_table_exists(oop class_loader) {
assert(met != nullptr, "sanity"); assert(met != nullptr, "sanity");
} }
void ClassLoaderDataShared::iterate_symbols(MetaspaceClosure* closure) { void ClassLoaderDataShared::build_tables(TRAPS) {
assert(CDSConfig::is_dumping_full_module_graph(), "must be"); assert(CDSConfig::is_dumping_full_module_graph(), "must be");
_archived_boot_loader_data.iterate_symbols (null_class_loader_data(), closure); _archived_boot_loader_data.build_tables(null_class_loader_data(), CHECK);
_archived_platform_loader_data.iterate_symbols(java_platform_loader_data_or_null(), closure); _archived_platform_loader_data.build_tables(java_platform_loader_data_or_null(), CHECK);
_archived_system_loader_data.iterate_symbols (java_system_loader_data_or_null(), closure); _archived_system_loader_data.build_tables(java_system_loader_data_or_null(), CHECK);
} }
void ClassLoaderDataShared::allocate_archived_tables() { void ClassLoaderDataShared::iterate_roots(MetaspaceClosure* it) {
assert(CDSConfig::is_dumping_full_module_graph(), "must be"); assert(CDSConfig::is_dumping_full_module_graph(), "must be");
_archived_boot_loader_data.allocate (null_class_loader_data()); _archived_boot_loader_data.iterate_roots(it);
_archived_platform_loader_data.allocate(java_platform_loader_data_or_null()); _archived_platform_loader_data.iterate_roots(it);
_archived_system_loader_data.allocate (java_system_loader_data_or_null()); _archived_system_loader_data.iterate_roots(it);
} }
void ClassLoaderDataShared::init_archived_tables() { void ClassLoaderDataShared::remove_unshareable_info() {
assert(CDSConfig::is_dumping_full_module_graph(), "must be"); assert(CDSConfig::is_dumping_full_module_graph(), "must be");
_archived_boot_loader_data.remove_unshareable_info();
_archived_platform_loader_data.remove_unshareable_info();
_archived_system_loader_data.remove_unshareable_info();
_archived_boot_loader_data.init_archived_entries (null_class_loader_data()); _archived_javabase_moduleEntry = ArchiveBuilder::current()->get_buffered_addr(ModuleEntryTable::javabase_moduleEntry());
_archived_platform_loader_data.init_archived_entries(java_platform_loader_data_or_null());
_archived_system_loader_data.init_archived_entries (java_system_loader_data_or_null());
_archived_javabase_moduleEntry = ModuleEntry::get_archived_entry(ModuleEntryTable::javabase_moduleEntry());
_platform_loader_root_index = HeapShared::append_root(SystemDictionary::java_platform_loader()); _platform_loader_root_index = HeapShared::append_root(SystemDictionary::java_platform_loader());
_system_loader_root_index = HeapShared::append_root(SystemDictionary::java_system_loader()); _system_loader_root_index = HeapShared::append_root(SystemDictionary::java_system_loader());
@ -271,7 +275,6 @@ ModuleEntry* ClassLoaderDataShared::archived_unnamed_module(ClassLoaderData* loa
return archived_module; return archived_module;
} }
void ClassLoaderDataShared::clear_archived_oops() { void ClassLoaderDataShared::clear_archived_oops() {
assert(!CDSConfig::is_using_full_module_graph(), "must be"); assert(!CDSConfig::is_using_full_module_graph(), "must be");
_archived_boot_loader_data.clear_archived_oops(); _archived_boot_loader_data.clear_archived_oops();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2020, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -40,11 +40,11 @@ class ClassLoaderDataShared : AllStatic {
public: public:
static void load_archived_platform_and_system_class_loaders() NOT_CDS_JAVA_HEAP_RETURN; static void load_archived_platform_and_system_class_loaders() NOT_CDS_JAVA_HEAP_RETURN;
static void restore_archived_modules_for_preloading_classes(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN; static void restore_archived_modules_for_preloading_classes(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN;
static void build_tables(TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
static void iterate_roots(MetaspaceClosure* closure) NOT_CDS_JAVA_HEAP_RETURN;
static void remove_unshareable_info() NOT_CDS_JAVA_HEAP_RETURN;
#if INCLUDE_CDS_JAVA_HEAP #if INCLUDE_CDS_JAVA_HEAP
static void ensure_module_entry_tables_exist(); static void ensure_module_entry_tables_exist();
static void allocate_archived_tables();
static void iterate_symbols(MetaspaceClosure* closure);
static void init_archived_tables();
static void serialize(SerializeClosure* f); static void serialize(SerializeClosure* f);
static void clear_archived_oops(); static void clear_archived_oops();
static void restore_archived_entries_for_null_class_loader_data(); static void restore_archived_entries_for_null_class_loader_data();

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -23,6 +23,7 @@
*/ */
#include "cds/aotClassLocation.hpp" #include "cds/aotClassLocation.hpp"
#include "cds/aotGrowableArray.inline.hpp"
#include "cds/archiveBuilder.hpp" #include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp" #include "cds/archiveUtils.hpp"
#include "cds/cdsConfig.hpp" #include "cds/cdsConfig.hpp"
@ -37,6 +38,7 @@
#include "jni.h" #include "jni.h"
#include "logging/log.hpp" #include "logging/log.hpp"
#include "logging/logStream.hpp" #include "logging/logStream.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "memory/universe.hpp" #include "memory/universe.hpp"
#include "oops/oopHandle.inline.hpp" #include "oops/oopHandle.inline.hpp"
@ -44,7 +46,6 @@
#include "runtime/handles.inline.hpp" #include "runtime/handles.inline.hpp"
#include "runtime/safepoint.hpp" #include "runtime/safepoint.hpp"
#include "utilities/events.hpp" #include "utilities/events.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashTable.hpp" #include "utilities/hashTable.hpp"
#include "utilities/ostream.hpp" #include "utilities/ostream.hpp"
#include "utilities/quickSort.hpp" #include "utilities/quickSort.hpp"
@ -167,7 +168,7 @@ void ModuleEntry::add_read(ModuleEntry* m) {
} else { } else {
if (reads() == nullptr) { if (reads() == nullptr) {
// Lazily create a module's reads list // Lazily create a module's reads list
GrowableArray<ModuleEntry*>* new_reads = new (mtModule) GrowableArray<ModuleEntry*>(MODULE_READS_SIZE, mtModule); AOTGrowableArray<ModuleEntry*>* new_reads = new (mtModule) AOTGrowableArray<ModuleEntry*>(MODULE_READS_SIZE, mtModule);
set_reads(new_reads); set_reads(new_reads);
} }
@ -274,8 +275,7 @@ ModuleEntry::ModuleEntry(Handle module_handle,
_has_default_read_edges(false), _has_default_read_edges(false),
_must_walk_reads(false), _must_walk_reads(false),
_is_open(is_open), _is_open(is_open),
_is_patched(false) _is_patched(false) {
DEBUG_ONLY(COMMA _reads_is_archived(false)) {
// Initialize fields specific to a ModuleEntry // Initialize fields specific to a ModuleEntry
if (_name == nullptr) { if (_name == nullptr) {
@ -394,7 +394,6 @@ ModuleEntryTable::~ModuleEntryTable() {
ModuleEntryTableDeleter deleter; ModuleEntryTableDeleter deleter;
_table.unlink(&deleter); _table.unlink(&deleter);
assert(_table.number_of_entries() == 0, "should have removed all entries"); assert(_table.number_of_entries() == 0, "should have removed all entries");
} }
void ModuleEntry::set_loader_data(ClassLoaderData* cld) { void ModuleEntry::set_loader_data(ClassLoaderData* cld) {
@ -402,147 +401,51 @@ void ModuleEntry::set_loader_data(ClassLoaderData* cld) {
_loader_data = cld; _loader_data = cld;
} }
void ModuleEntry::metaspace_pointers_do(MetaspaceClosure* it) {
it->push(&_name);
it->push(&_reads);
it->push(&_version);
it->push(&_location);
}
#if INCLUDE_CDS_JAVA_HEAP #if INCLUDE_CDS_JAVA_HEAP
typedef HashTable<
const ModuleEntry*,
ModuleEntry*,
557, // prime number
AnyObj::C_HEAP> ArchivedModuleEntries;
static ArchivedModuleEntries* _archive_modules_entries = nullptr;
#ifndef PRODUCT
static int _num_archived_module_entries = 0;
static int _num_inited_module_entries = 0;
#endif
bool ModuleEntry::should_be_archived() const { bool ModuleEntry::should_be_archived() const {
return SystemDictionaryShared::is_builtin_loader(loader_data()); return SystemDictionaryShared::is_builtin_loader(loader_data());
} }
ModuleEntry* ModuleEntry::allocate_archived_entry() const { void ModuleEntry::remove_unshareable_info() {
precond(should_be_archived()); _archived_module_index = HeapShared::append_root(module_oop());
precond(CDSConfig::is_dumping_full_module_graph());
ModuleEntry* archived_entry = (ModuleEntry*)ArchiveBuilder::rw_region_alloc(sizeof(ModuleEntry));
memcpy((void*)archived_entry, (void*)this, sizeof(ModuleEntry));
archived_entry->_archived_module_index = HeapShared::append_root(module_oop()); if (_reads != nullptr) {
if (_archive_modules_entries == nullptr) { _reads->set_in_aot_cache();
_archive_modules_entries = new (mtClass)ArchivedModuleEntries();
}
assert(_archive_modules_entries->get(this) == nullptr, "Each ModuleEntry must not be shared across ModuleEntryTables");
_archive_modules_entries->put(this, archived_entry);
DEBUG_ONLY(_num_archived_module_entries++);
if (CDSConfig::is_dumping_final_static_archive()) {
OopHandle null_handle;
archived_entry->_shared_pd = null_handle;
} else {
assert(archived_entry->shared_protection_domain() == nullptr, "never set during -Xshare:dump");
} }
// Clear handles and restore at run time. Handles cannot be archived. // Clear handles and restore at run time. Handles cannot be archived.
if (CDSConfig::is_dumping_final_static_archive()) {
OopHandle null_handle;
_shared_pd = null_handle;
} else {
assert(shared_protection_domain() == nullptr, "never set during -Xshare:dump");
}
OopHandle null_handle; OopHandle null_handle;
archived_entry->_module_handle = null_handle; _module_handle = null_handle;
// For verify_archived_module_entries()
DEBUG_ONLY(_num_inited_module_entries++);
if (log_is_enabled(Info, aot, module)) {
ResourceMark rm;
LogStream ls(Log(aot, module)::info());
ls.print("Stored in archive: ");
archived_entry->print(&ls);
}
return archived_entry;
}
bool ModuleEntry::has_been_archived() {
assert(!ArchiveBuilder::current()->is_in_buffer_space(this), "must be called on original ModuleEntry");
return _archive_modules_entries->contains(this);
}
ModuleEntry* ModuleEntry::get_archived_entry(ModuleEntry* orig_entry) {
ModuleEntry** ptr = _archive_modules_entries->get(orig_entry);
assert(ptr != nullptr && *ptr != nullptr, "must have been allocated");
return *ptr;
}
// This function is used to archive ModuleEntry::_reads and PackageEntry::_qualified_exports.
// GrowableArray cannot be directly archived, as it needs to be expandable at runtime.
// Write it out as an Array, and convert it back to GrowableArray at runtime.
Array<ModuleEntry*>* ModuleEntry::write_growable_array(GrowableArray<ModuleEntry*>* array) {
Array<ModuleEntry*>* archived_array = nullptr;
int length = (array == nullptr) ? 0 : array->length();
if (length > 0) {
archived_array = ArchiveBuilder::new_ro_array<ModuleEntry*>(length);
for (int i = 0; i < length; i++) {
ModuleEntry* archived_entry = get_archived_entry(array->at(i));
archived_array->at_put(i, archived_entry);
ArchivePtrMarker::mark_pointer((address*)archived_array->adr_at(i));
}
}
return archived_array;
}
GrowableArray<ModuleEntry*>* ModuleEntry::restore_growable_array(Array<ModuleEntry*>* archived_array) {
GrowableArray<ModuleEntry*>* array = nullptr;
int length = (archived_array == nullptr) ? 0 : archived_array->length();
if (length > 0) {
array = new (mtModule) GrowableArray<ModuleEntry*>(length, mtModule);
for (int i = 0; i < length; i++) {
ModuleEntry* archived_entry = archived_array->at(i);
array->append(archived_entry);
}
}
return array;
}
void ModuleEntry::iterate_symbols(MetaspaceClosure* closure) {
closure->push(&_name);
closure->push(&_version);
closure->push(&_location);
}
void ModuleEntry::init_as_archived_entry() {
set_archived_reads(write_growable_array(reads()));
_loader_data = nullptr; // re-init at runtime _loader_data = nullptr; // re-init at runtime
if (name() != nullptr) { if (name() != nullptr) {
_shared_path_index = AOTClassLocationConfig::dumptime()->get_module_shared_path_index(_location); Symbol* src_location = ArchiveBuilder::current()->get_source_addr(_location);
_name = ArchiveBuilder::get_buffered_symbol(_name); _shared_path_index = AOTClassLocationConfig::dumptime()->get_module_shared_path_index(src_location);
ArchivePtrMarker::mark_pointer((address*)&_name);
} else { } else {
// _shared_path_index is used only by SystemDictionary::is_shared_class_visible_impl() // _shared_path_index is used only by SystemDictionary::is_shared_class_visible_impl()
// for checking classes in named modules. // for checking classes in named modules.
_shared_path_index = -1; _shared_path_index = -1;
} }
if (_version != nullptr) {
_version = ArchiveBuilder::get_buffered_symbol(_version);
}
if (_location != nullptr) {
_location = ArchiveBuilder::get_buffered_symbol(_location);
}
JFR_ONLY(set_trace_id(0);) // re-init at runtime JFR_ONLY(set_trace_id(0);) // re-init at runtime
ArchivePtrMarker::mark_pointer((address*)&_reads);
ArchivePtrMarker::mark_pointer((address*)&_version);
ArchivePtrMarker::mark_pointer((address*)&_location);
} }
#ifndef PRODUCT
void ModuleEntry::verify_archived_module_entries() {
assert(_num_archived_module_entries == _num_inited_module_entries,
"%d ModuleEntries have been archived but %d of them have been properly initialized with archived java.lang.Module objects",
_num_archived_module_entries, _num_inited_module_entries);
}
#endif // PRODUCT
void ModuleEntry::load_from_archive(ClassLoaderData* loader_data) { void ModuleEntry::load_from_archive(ClassLoaderData* loader_data) {
assert(CDSConfig::is_using_archive(), "runtime only"); assert(CDSConfig::is_using_archive(), "runtime only");
set_loader_data(loader_data); set_loader_data(loader_data);
set_reads(restore_growable_array(archived_reads()));
JFR_ONLY(INIT_ID(this);) JFR_ONLY(INIT_ID(this);)
} }
@ -581,38 +484,28 @@ static int compare_module_by_name(ModuleEntry* a, ModuleEntry* b) {
return a->name()->fast_compare(b->name()); return a->name()->fast_compare(b->name());
} }
void ModuleEntryTable::iterate_symbols(MetaspaceClosure* closure) { Array<ModuleEntry*>* ModuleEntryTable::build_aot_table(ClassLoaderData* loader_data, TRAPS) {
auto syms = [&] (const SymbolHandle& key, ModuleEntry*& m) { Array<ModuleEntry*>* aot_table =
m->iterate_symbols(closure); MetadataFactory::new_array<ModuleEntry*>(loader_data, _table.number_of_entries(), nullptr, CHECK_NULL);
};
_table.iterate_all(syms);
}
Array<ModuleEntry*>* ModuleEntryTable::allocate_archived_entries() {
Array<ModuleEntry*>* archived_modules = ArchiveBuilder::new_rw_array<ModuleEntry*>(_table.number_of_entries());
int n = 0; int n = 0;
auto grab = [&] (const SymbolHandle& key, ModuleEntry*& m) { auto grab = [&] (const SymbolHandle& key, ModuleEntry*& m) {
archived_modules->at_put(n++, m); m->pack_reads();
aot_table->at_put(n++, m);
if (log_is_enabled(Info, aot, module)) {
ResourceMark rm;
LogStream ls(Log(aot, module)::info());
ls.print("Stored in archive: ");
m->print(&ls);
}
}; };
_table.iterate_all(grab); _table.iterate_all(grab);
if (n > 1) { if (n > 1) {
// Always allocate in the same order to produce deterministic archive. // Always allocate in the same order to produce deterministic archive.
QuickSort::sort(archived_modules->data(), n, compare_module_by_name); QuickSort::sort(aot_table->data(), n, compare_module_by_name);
} }
for (int i = 0; i < n; i++) {
archived_modules->at_put(i, archived_modules->at(i)->allocate_archived_entry());
ArchivePtrMarker::mark_pointer((address*)archived_modules->adr_at(i));
}
return archived_modules;
}
void ModuleEntryTable::init_archived_entries(Array<ModuleEntry*>* archived_modules) { return aot_table;
assert(CDSConfig::is_dumping_full_module_graph(), "sanity");
for (int i = 0; i < archived_modules->length(); i++) {
ModuleEntry* archived_entry = archived_modules->at(i);
archived_entry->init_as_archived_entry();
}
} }
void ModuleEntryTable::load_archived_entries(ClassLoaderData* loader_data, void ModuleEntryTable::load_archived_entries(ClassLoaderData* loader_data,

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
#ifndef SHARE_CLASSFILE_MODULEENTRY_HPP #ifndef SHARE_CLASSFILE_MODULEENTRY_HPP
#define SHARE_CLASSFILE_MODULEENTRY_HPP #define SHARE_CLASSFILE_MODULEENTRY_HPP
#include "cds/aotGrowableArray.hpp"
#include "jni.h" #include "jni.h"
#include "memory/metaspaceClosureType.hpp"
#include "oops/oopHandle.hpp" #include "oops/oopHandle.hpp"
#include "oops/symbol.hpp" #include "oops/symbol.hpp"
#include "oops/symbolHandle.hpp" #include "oops/symbolHandle.hpp"
@ -68,11 +70,8 @@ private:
// for shared classes from this module // for shared classes from this module
Symbol* _name; // name of this module Symbol* _name; // name of this module
ClassLoaderData* _loader_data; ClassLoaderData* _loader_data;
AOTGrowableArray<ModuleEntry*>* _reads; // list of modules that are readable by this module
union {
GrowableArray<ModuleEntry*>* _reads; // list of modules that are readable by this module
Array<ModuleEntry*>* _archived_reads; // List of readable modules stored in the CDS archive
};
Symbol* _version; // module version number Symbol* _version; // module version number
Symbol* _location; // module location Symbol* _location; // module location
CDS_ONLY(int _shared_path_index;) // >=0 if classes in this module are in CDS archive CDS_ONLY(int _shared_path_index;) // >=0 if classes in this module are in CDS archive
@ -81,7 +80,6 @@ private:
bool _must_walk_reads; // walk module's reads list at GC safepoints to purge out dead modules bool _must_walk_reads; // walk module's reads list at GC safepoints to purge out dead modules
bool _is_open; // whether the packages in the module are all unqualifiedly exported bool _is_open; // whether the packages in the module are all unqualifiedly exported
bool _is_patched; // whether the module is patched via --patch-module bool _is_patched; // whether the module is patched via --patch-module
DEBUG_ONLY(bool _reads_is_archived);
CDS_JAVA_HEAP_ONLY(int _archived_module_index;) CDS_JAVA_HEAP_ONLY(int _archived_module_index;)
JFR_ONLY(DEFINE_TRACE_ID_FIELD;) JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
@ -120,22 +118,18 @@ public:
bool can_read(ModuleEntry* m) const; bool can_read(ModuleEntry* m) const;
bool has_reads_list() const; bool has_reads_list() const;
GrowableArray<ModuleEntry*>* reads() const { AOTGrowableArray<ModuleEntry*>* reads() const {
assert(!_reads_is_archived, "sanity");
return _reads; return _reads;
} }
void set_reads(GrowableArray<ModuleEntry*>* r) { void set_reads(AOTGrowableArray<ModuleEntry*>* r) {
_reads = r; _reads = r;
DEBUG_ONLY(_reads_is_archived = false);
} }
Array<ModuleEntry*>* archived_reads() const { void pack_reads() {
assert(_reads_is_archived, "sanity"); if (_reads != nullptr) {
return _archived_reads; _reads->shrink_to_fit();
} }
void set_archived_reads(Array<ModuleEntry*>* r) {
_archived_reads = r;
DEBUG_ONLY(_reads_is_archived = true);
} }
void add_read(ModuleEntry* m); void add_read(ModuleEntry* m);
void set_read_walk_required(ClassLoaderData* m_loader_data); void set_read_walk_required(ClassLoaderData* m_loader_data);
@ -189,6 +183,13 @@ public:
const char* name_as_C_string() const { const char* name_as_C_string() const {
return is_named() ? name()->as_C_string() : UNNAMED_MODULE; return is_named() ? name()->as_C_string() : UNNAMED_MODULE;
} }
// methods required by MetaspaceClosure
void metaspace_pointers_do(MetaspaceClosure* it);
int size_in_heapwords() const { return (int)heap_word_size(sizeof(ModuleEntry)); }
MetaspaceClosureType type() const { return MetaspaceClosureType::ModuleEntryType; }
static bool is_read_only_by_default() { return false; }
void print(outputStream* st = tty) const; void print(outputStream* st = tty) const;
void verify(); void verify();
@ -198,18 +199,11 @@ public:
#if INCLUDE_CDS_JAVA_HEAP #if INCLUDE_CDS_JAVA_HEAP
bool should_be_archived() const; bool should_be_archived() const;
void iterate_symbols(MetaspaceClosure* closure); void remove_unshareable_info();
ModuleEntry* allocate_archived_entry() const;
void init_as_archived_entry();
static ModuleEntry* get_archived_entry(ModuleEntry* orig_entry);
bool has_been_archived();
static Array<ModuleEntry*>* write_growable_array(GrowableArray<ModuleEntry*>* array);
static GrowableArray<ModuleEntry*>* restore_growable_array(Array<ModuleEntry*>* archived_array);
void load_from_archive(ClassLoaderData* loader_data); void load_from_archive(ClassLoaderData* loader_data);
void preload_archived_oops(); void preload_archived_oops();
void restore_archived_oops(ClassLoaderData* loader_data); void restore_archived_oops(ClassLoaderData* loader_data);
void clear_archived_oops(); void clear_archived_oops();
static void verify_archived_module_entries() PRODUCT_RETURN;
#endif #endif
}; };
@ -275,9 +269,7 @@ public:
void verify(); void verify();
#if INCLUDE_CDS_JAVA_HEAP #if INCLUDE_CDS_JAVA_HEAP
void iterate_symbols(MetaspaceClosure* closure); Array<ModuleEntry*>* build_aot_table(ClassLoaderData* loader_data, TRAPS);
Array<ModuleEntry*>* allocate_archived_entries();
void init_archived_entries(Array<ModuleEntry*>* archived_modules);
void load_archived_entries(ClassLoaderData* loader_data, void load_archived_entries(ClassLoaderData* loader_data,
Array<ModuleEntry*>* archived_modules); Array<ModuleEntry*>* archived_modules);
void restore_archived_oops(ClassLoaderData* loader_data, void restore_archived_oops(ClassLoaderData* loader_data,

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -505,13 +505,10 @@ void Modules::check_archived_module_oop(oop orig_module_obj) {
ClassLoaderData* loader_data = orig_module_ent->loader_data(); ClassLoaderData* loader_data = orig_module_ent->loader_data();
assert(loader_data->is_builtin_class_loader_data(), "must be"); assert(loader_data->is_builtin_class_loader_data(), "must be");
if (orig_module_ent->name() != nullptr) { precond(ArchiveBuilder::current()->has_been_archived(orig_module_ent));
// For each named module, we archive both the java.lang.Module oop and the ModuleEntry. if (orig_module_ent->name() == nullptr) {
assert(orig_module_ent->has_been_archived(), "sanity");
} else {
// We always archive unnamed module oop for boot, platform, and system loaders. // We always archive unnamed module oop for boot, platform, and system loaders.
precond(orig_module_ent->should_be_archived()); precond(orig_module_ent->should_be_archived());
precond(orig_module_ent->has_been_archived());
if (loader_data->is_boot_class_loader_data()) { if (loader_data->is_boot_class_loader_data()) {
assert(!_seen_boot_unnamed_module, "only once"); assert(!_seen_boot_unnamed_module, "only once");
@ -529,10 +526,6 @@ void Modules::check_archived_module_oop(oop orig_module_obj) {
} }
} }
void Modules::verify_archived_modules() {
ModuleEntry::verify_archived_module_entries();
}
class Modules::ArchivedProperty { class Modules::ArchivedProperty {
const char* _prop; const char* _prop;
const bool _numbered; const bool _numbered;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -59,7 +59,6 @@ public:
TRAPS) NOT_CDS_JAVA_HEAP_RETURN; TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
static void init_archived_modules(JavaThread* current, Handle h_platform_loader, Handle h_system_loader) static void init_archived_modules(JavaThread* current, Handle h_platform_loader, Handle h_system_loader)
NOT_CDS_JAVA_HEAP_RETURN; NOT_CDS_JAVA_HEAP_RETURN;
static void verify_archived_modules() NOT_CDS_JAVA_HEAP_RETURN;
static void dump_archived_module_info() NOT_CDS_JAVA_HEAP_RETURN; static void dump_archived_module_info() NOT_CDS_JAVA_HEAP_RETURN;
static void serialize_archived_module_info(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN; static void serialize_archived_module_info(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -22,6 +22,8 @@
* *
*/ */
#include "cds/aotGrowableArray.inline.hpp"
#include "cds/aotMetaspace.hpp"
#include "cds/archiveBuilder.hpp" #include "cds/archiveBuilder.hpp"
#include "cds/archiveUtils.hpp" #include "cds/archiveUtils.hpp"
#include "cds/cdsConfig.hpp" #include "cds/cdsConfig.hpp"
@ -31,13 +33,13 @@
#include "classfile/vmSymbols.hpp" #include "classfile/vmSymbols.hpp"
#include "logging/log.hpp" #include "logging/log.hpp"
#include "logging/logStream.hpp" #include "logging/logStream.hpp"
#include "memory/metadataFactory.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "oops/array.hpp" #include "oops/array.hpp"
#include "oops/symbol.hpp" #include "oops/symbol.hpp"
#include "runtime/handles.inline.hpp" #include "runtime/handles.inline.hpp"
#include "runtime/java.hpp" #include "runtime/java.hpp"
#include "utilities/events.hpp" #include "utilities/events.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/hashTable.hpp" #include "utilities/hashTable.hpp"
#include "utilities/ostream.hpp" #include "utilities/ostream.hpp"
#include "utilities/quickSort.hpp" #include "utilities/quickSort.hpp"
@ -51,7 +53,7 @@ PackageEntry::PackageEntry(Symbol* name, ModuleEntry* module) :
_qualified_exports(nullptr), _qualified_exports(nullptr),
_defined_by_cds_in_class_path(0) _defined_by_cds_in_class_path(0)
{ {
// name can't be null // name can't be null -- a class in the default package gets a PackageEntry of nullptr.
_name->increment_refcount(); _name->increment_refcount();
JFR_ONLY(INIT_ID(this);) JFR_ONLY(INIT_ID(this);)
@ -81,7 +83,7 @@ void PackageEntry::add_qexport(ModuleEntry* m) {
if (!has_qual_exports_list()) { if (!has_qual_exports_list()) {
// Lazily create a package's qualified exports list. // Lazily create a package's qualified exports list.
// Initial size is small, do not anticipate export lists to be large. // Initial size is small, do not anticipate export lists to be large.
_qualified_exports = new (mtModule) GrowableArray<ModuleEntry*>(QUAL_EXP_SIZE, mtModule); _qualified_exports = new (mtModule) AOTGrowableArray<ModuleEntry*>(QUAL_EXP_SIZE, mtModule);
} }
// Determine, based on this newly established export to module m, // Determine, based on this newly established export to module m,
@ -183,12 +185,24 @@ void PackageEntry::purge_qualified_exports() {
} }
void PackageEntry::delete_qualified_exports() { void PackageEntry::delete_qualified_exports() {
if (_qualified_exports != nullptr) { if (_qualified_exports != nullptr && !AOTMetaspace::in_aot_cache(_qualified_exports)) {
delete _qualified_exports; delete _qualified_exports;
} }
_qualified_exports = nullptr; _qualified_exports = nullptr;
} }
void PackageEntry::pack_qualified_exports() {
if (_qualified_exports != nullptr) {
_qualified_exports->shrink_to_fit();
}
}
void PackageEntry::metaspace_pointers_do(MetaspaceClosure* it) {
it->push(&_name);
it->push(&_module);
it->push(&_qualified_exports);
}
PackageEntryTable::PackageEntryTable() { } PackageEntryTable::PackageEntryTable() { }
PackageEntryTable::~PackageEntryTable() { PackageEntryTable::~PackageEntryTable() {
@ -212,66 +226,19 @@ PackageEntryTable::~PackageEntryTable() {
} }
#if INCLUDE_CDS_JAVA_HEAP #if INCLUDE_CDS_JAVA_HEAP
typedef HashTable<
const PackageEntry*,
PackageEntry*,
557, // prime number
AnyObj::C_HEAP> ArchivedPackageEntries;
static ArchivedPackageEntries* _archived_packages_entries = nullptr;
bool PackageEntry::should_be_archived() const { bool PackageEntry::should_be_archived() const {
return module()->should_be_archived(); return module()->should_be_archived();
} }
PackageEntry* PackageEntry::allocate_archived_entry() const { void PackageEntry::remove_unshareable_info() {
precond(should_be_archived()); if (_qualified_exports != nullptr) {
PackageEntry* archived_entry = (PackageEntry*)ArchiveBuilder::rw_region_alloc(sizeof(PackageEntry)); _qualified_exports->set_in_aot_cache();
memcpy((void*)archived_entry, (void*)this, sizeof(PackageEntry));
if (_archived_packages_entries == nullptr) {
_archived_packages_entries = new (mtClass)ArchivedPackageEntries();
} }
assert(_archived_packages_entries->get(this) == nullptr, "Each PackageEntry must not be shared across PackageEntryTables");
_archived_packages_entries->put(this, archived_entry);
return archived_entry;
}
PackageEntry* PackageEntry::get_archived_entry(PackageEntry* orig_entry) {
PackageEntry** ptr = _archived_packages_entries->get(orig_entry);
if (ptr != nullptr) {
return *ptr;
} else {
return nullptr;
}
}
void PackageEntry::iterate_symbols(MetaspaceClosure* closure) {
closure->push(&_name);
}
void PackageEntry::init_as_archived_entry() {
Array<ModuleEntry*>* archived_qualified_exports = ModuleEntry::write_growable_array(_qualified_exports);
_name = ArchiveBuilder::get_buffered_symbol(_name);
_module = ModuleEntry::get_archived_entry(_module);
_qualified_exports = (GrowableArray<ModuleEntry*>*)archived_qualified_exports;
_defined_by_cds_in_class_path = 0; _defined_by_cds_in_class_path = 0;
JFR_ONLY(set_trace_id(0);) // re-init at runtime JFR_ONLY(set_trace_id(0);) // re-init at runtime
ArchivePtrMarker::mark_pointer((address*)&_name);
ArchivePtrMarker::mark_pointer((address*)&_module);
ArchivePtrMarker::mark_pointer((address*)&_qualified_exports);
LogStreamHandle(Info, aot, package) st;
if (st.is_enabled()) {
st.print("archived ");
print(&st);
}
} }
void PackageEntry::load_from_archive() { void PackageEntry::load_from_archive() {
_qualified_exports = ModuleEntry::restore_growable_array((Array<ModuleEntry*>*)_qualified_exports);
JFR_ONLY(INIT_ID(this);) JFR_ONLY(INIT_ID(this);)
} }
@ -280,14 +247,7 @@ static int compare_package_by_name(PackageEntry* a, PackageEntry* b) {
return a->name()->fast_compare(b->name()); return a->name()->fast_compare(b->name());
} }
void PackageEntryTable::iterate_symbols(MetaspaceClosure* closure) { Array<PackageEntry*>* PackageEntryTable::build_aot_table(ClassLoaderData* loader_data, TRAPS) {
auto syms = [&] (const SymbolHandle& key, PackageEntry*& p) {
p->iterate_symbols(closure);
};
_table.iterate_all(syms);
}
Array<PackageEntry*>* PackageEntryTable::allocate_archived_entries() {
// First count the packages in named modules // First count the packages in named modules
int n = 0; int n = 0;
auto count = [&] (const SymbolHandle& key, PackageEntry*& p) { auto count = [&] (const SymbolHandle& key, PackageEntry*& p) {
@ -297,12 +257,19 @@ Array<PackageEntry*>* PackageEntryTable::allocate_archived_entries() {
}; };
_table.iterate_all(count); _table.iterate_all(count);
Array<PackageEntry*>* archived_packages = ArchiveBuilder::new_rw_array<PackageEntry*>(n); Array<PackageEntry*>* archived_packages = MetadataFactory::new_array<PackageEntry*>(loader_data, n, nullptr, CHECK_NULL);
// reset n // reset n
n = 0; n = 0;
auto grab = [&] (const SymbolHandle& key, PackageEntry*& p) { auto grab = [&] (const SymbolHandle& key, PackageEntry*& p) {
if (p->should_be_archived()) { if (p->should_be_archived()) {
p->pack_qualified_exports();
archived_packages->at_put(n++, p); archived_packages->at_put(n++, p);
LogStreamHandle(Info, aot, package) st;
if (st.is_enabled()) {
st.print("archived ");
p->print(&st);
}
} }
}; };
_table.iterate_all(grab); _table.iterate_all(grab);
@ -311,18 +278,8 @@ Array<PackageEntry*>* PackageEntryTable::allocate_archived_entries() {
// Always allocate in the same order to produce deterministic archive. // Always allocate in the same order to produce deterministic archive.
QuickSort::sort(archived_packages->data(), n, compare_package_by_name); QuickSort::sort(archived_packages->data(), n, compare_package_by_name);
} }
for (int i = 0; i < n; i++) {
archived_packages->at_put(i, archived_packages->at(i)->allocate_archived_entry());
ArchivePtrMarker::mark_pointer((address*)archived_packages->adr_at(i));
}
return archived_packages;
}
void PackageEntryTable::init_archived_entries(Array<PackageEntry*>* archived_packages) { return archived_packages;
for (int i = 0; i < archived_packages->length(); i++) {
PackageEntry* archived_entry = archived_packages->at(i);
archived_entry->init_as_archived_entry();
}
} }
void PackageEntryTable::load_archived_entries(Array<PackageEntry*>* archived_packages) { void PackageEntryTable::load_archived_entries(Array<PackageEntry*>* archived_packages) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2016, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,7 +25,9 @@
#ifndef SHARE_CLASSFILE_PACKAGEENTRY_HPP #ifndef SHARE_CLASSFILE_PACKAGEENTRY_HPP
#define SHARE_CLASSFILE_PACKAGEENTRY_HPP #define SHARE_CLASSFILE_PACKAGEENTRY_HPP
#include "cds/aotGrowableArray.hpp"
#include "classfile/moduleEntry.hpp" #include "classfile/moduleEntry.hpp"
#include "memory/metaspaceClosureType.hpp"
#include "oops/symbol.hpp" #include "oops/symbol.hpp"
#include "oops/symbolHandle.hpp" #include "oops/symbolHandle.hpp"
#include "runtime/atomicAccess.hpp" #include "runtime/atomicAccess.hpp"
@ -114,7 +116,7 @@ private:
bool _must_walk_exports; bool _must_walk_exports;
// Contains list of modules this package is qualifiedly exported to. Access // Contains list of modules this package is qualifiedly exported to. Access
// to this list is protected by the Module_lock. // to this list is protected by the Module_lock.
GrowableArray<ModuleEntry*>* _qualified_exports; AOTGrowableArray<ModuleEntry*>* _qualified_exports;
JFR_ONLY(DEFINE_TRACE_ID_FIELD;) JFR_ONLY(DEFINE_TRACE_ID_FIELD;)
// Initial size of a package entry's list of qualified exports. // Initial size of a package entry's list of qualified exports.
@ -205,14 +207,24 @@ public:
void purge_qualified_exports(); void purge_qualified_exports();
void delete_qualified_exports(); void delete_qualified_exports();
void pack_qualified_exports(); // used by AOT
// methods required by MetaspaceClosure
void metaspace_pointers_do(MetaspaceClosure* it);
int size_in_heapwords() const { return (int)heap_word_size(sizeof(PackageEntry)); }
MetaspaceClosureType type() const { return MetaspaceClosureType::PackageEntryType; }
static bool is_read_only_by_default() { return false; }
void print(outputStream* st = tty); void print(outputStream* st = tty);
char* name_as_C_string() const {
assert(_name != nullptr, "name can't be null");
return name()->as_C_string();
}
#if INCLUDE_CDS_JAVA_HEAP #if INCLUDE_CDS_JAVA_HEAP
bool should_be_archived() const; bool should_be_archived() const;
void iterate_symbols(MetaspaceClosure* closure); void remove_unshareable_info();
PackageEntry* allocate_archived_entry() const;
void init_as_archived_entry();
static PackageEntry* get_archived_entry(PackageEntry* orig_entry);
void load_from_archive(); void load_from_archive();
#endif #endif
@ -271,9 +283,7 @@ public:
void print(outputStream* st = tty); void print(outputStream* st = tty);
#if INCLUDE_CDS_JAVA_HEAP #if INCLUDE_CDS_JAVA_HEAP
void iterate_symbols(MetaspaceClosure* closure); Array<PackageEntry*>* build_aot_table(ClassLoaderData* loader_data, TRAPS);
Array<PackageEntry*>* allocate_archived_entries();
void init_archived_entries(Array<PackageEntry*>* archived_packages);
void load_archived_entries(Array<PackageEntry*>* archived_packages); void load_archived_entries(Array<PackageEntry*>* archived_packages);
#endif #endif
}; };

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -22,6 +22,7 @@
* *
*/ */
#include "cds/aotMetaspace.hpp"
#include "memory/allocation.inline.hpp" #include "memory/allocation.inline.hpp"
#include "memory/arena.hpp" #include "memory/arena.hpp"
#include "memory/metaspace.hpp" #include "memory/metaspace.hpp"
@ -161,12 +162,33 @@ void AnyObj::set_allocation_type(address res, allocation_type type) {
} }
} }
void AnyObj::set_in_aot_cache() {
_allocation_t[0] = 0;
_allocation_t[1] = 0;
}
bool AnyObj::in_aot_cache() const {
if (AOTMetaspace::in_aot_cache(this)) {
precond(_allocation_t[0] == 0);
precond(_allocation_t[1] == 0);
return true;
} else {
return false;
}
}
AnyObj::allocation_type AnyObj::get_allocation_type() const { AnyObj::allocation_type AnyObj::get_allocation_type() const {
if (in_aot_cache()) {
return STACK_OR_EMBEDDED;
}
assert(~(_allocation_t[0] | allocation_mask) == (uintptr_t)this, "lost resource object"); assert(~(_allocation_t[0] | allocation_mask) == (uintptr_t)this, "lost resource object");
return (allocation_type)((~_allocation_t[0]) & allocation_mask); return (allocation_type)((~_allocation_t[0]) & allocation_mask);
} }
bool AnyObj::is_type_set() const { bool AnyObj::is_type_set() const {
if (in_aot_cache()) {
return true;
}
allocation_type type = (allocation_type)(_allocation_t[1] & allocation_mask); allocation_type type = (allocation_type)(_allocation_t[1] & allocation_mask);
return get_allocation_type() == type && return get_allocation_type() == type &&
(_allocation_t[1] - type) == (uintptr_t)(&_allocation_t[1]); (_allocation_t[1] - type) == (uintptr_t)(&_allocation_t[1]);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -420,6 +420,7 @@ class AnyObj {
public: public:
enum allocation_type { STACK_OR_EMBEDDED = 0, RESOURCE_AREA, C_HEAP, ARENA, allocation_mask = 0x3 }; enum allocation_type { STACK_OR_EMBEDDED = 0, RESOURCE_AREA, C_HEAP, ARENA, allocation_mask = 0x3 };
static void set_allocation_type(address res, allocation_type type) NOT_DEBUG_RETURN; static void set_allocation_type(address res, allocation_type type) NOT_DEBUG_RETURN;
void set_in_aot_cache() NOT_DEBUG_RETURN;
#ifdef ASSERT #ifdef ASSERT
private: private:
// When this object is allocated on stack the new() operator is not // When this object is allocated on stack the new() operator is not
@ -429,6 +430,7 @@ class AnyObj {
uintptr_t _allocation_t[2]; uintptr_t _allocation_t[2];
bool is_type_set() const; bool is_type_set() const;
void initialize_allocation_info(); void initialize_allocation_info();
bool in_aot_cache() const;
public: public:
allocation_type get_allocation_type() const; allocation_type get_allocation_type() const;
bool allocated_on_stack_or_embedded() const { return get_allocation_type() == STACK_OR_EMBEDDED; } bool allocated_on_stack_or_embedded() const { return get_allocation_type() == STACK_OR_EMBEDDED; }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -22,7 +22,21 @@
* *
*/ */
#include "cds/aotGrowableArray.hpp"
#include "classfile/packageEntry.hpp"
#include "memory/metaspaceClosure.hpp" #include "memory/metaspaceClosure.hpp"
#include "oops/array.hpp"
#include "oops/instanceKlass.hpp"
// Sanity checks
static_assert(!HAS_METASPACE_POINTERS_DO(int));
static_assert(HAS_METASPACE_POINTERS_DO(Array<int>));
static_assert(HAS_METASPACE_POINTERS_DO(Array<InstanceKlass*>));
static_assert(HAS_METASPACE_POINTERS_DO(InstanceKlass));
static_assert(HAS_METASPACE_POINTERS_DO(PackageEntry));
static_assert(HAS_METASPACE_POINTERS_DO(AOTGrowableArray<int>));
static_assert(HAS_METASPACE_POINTERS_DO(AOTGrowableArray<PackageEntry*>));
void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) { void MetaspaceClosure::push_impl(MetaspaceClosure::Ref* ref) {
if (_enclosing_ref != nullptr) { if (_enclosing_ref != nullptr) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2017, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -25,9 +25,11 @@
#ifndef SHARE_MEMORY_METASPACECLOSURE_HPP #ifndef SHARE_MEMORY_METASPACECLOSURE_HPP
#define SHARE_MEMORY_METASPACECLOSURE_HPP #define SHARE_MEMORY_METASPACECLOSURE_HPP
#include "cds/aotGrowableArray.hpp"
#include "cppstdlib/type_traits.hpp" #include "cppstdlib/type_traits.hpp"
#include "logging/log.hpp" #include "logging/log.hpp"
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "memory/metaspaceClosureType.hpp"
#include "metaprogramming/enableIf.hpp" #include "metaprogramming/enableIf.hpp"
#include "oops/array.hpp" #include "oops/array.hpp"
#include "utilities/debug.hpp" #include "utilities/debug.hpp"
@ -36,41 +38,27 @@
#include "utilities/macros.hpp" #include "utilities/macros.hpp"
#include "utilities/resizableHashTable.hpp" #include "utilities/resizableHashTable.hpp"
// The metadata hierarchy is separate from the oop hierarchy // This macro just check for the existence of a member with the name "metaspace_pointers_do". If the
class MetaspaceObj; // no C++ vtable // parameter list is not (MetaspaceClosure* it), you will get a compilation error.
//class Array; // no C++ vtable #define HAS_METASPACE_POINTERS_DO(T) HasMetaspacePointersDo<T>::value
class Annotations; // no C++ vtable
class ConstantPoolCache; // no C++ vtable template<typename T>
class ConstMethod; // no C++ vtable class HasMetaspacePointersDo {
class MethodCounters; // no C++ vtable template<typename U> static void* test(decltype(&U::metaspace_pointers_do));
class Symbol; // no C++ vtable template<typename> static int test(...);
class Metadata; // has C++ vtable (so do all subclasses) using test_type = decltype(test<T>(nullptr));
class ConstantPool; public:
class MethodData; static constexpr bool value = std::is_pointer_v<test_type>;
class Method; };
class Klass;
class InstanceKlass;
class InstanceMirrorKlass;
class InstanceClassLoaderKlass;
class InstanceRefKlass;
class ArrayKlass;
class ObjArrayKlass;
class TypeArrayKlass;
// class MetaspaceClosure -- // class MetaspaceClosure --
// //
// This class is used for iterating the objects in the HotSpot Metaspaces. It // This class is used for iterating the class metadata objects. It
// provides an API to walk all the reachable objects starting from a set of // provides an API to walk all the reachable objects starting from a set of
// root references (such as all Klass'es in the SystemDictionary). // root references (such as all Klass'es in the SystemDictionary).
// //
// Currently it is used for compacting the CDS archive by eliminate temporary // Currently it is used to copy class metadata into the AOT cache.
// objects allocated during archive creation time. See ArchiveBuilder for an example. // 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 { class MetaspaceClosure {
public: public:
enum Writability { enum Writability {
@ -79,16 +67,32 @@ public:
_default _default
}; };
#define METASPACE_CLOSURE_TYPE_NAME_CASE(name) case MetaspaceClosureType::name ## Type: return #name;
static const char* type_name(MetaspaceClosureType type) {
switch(type) {
METASPACE_CLOSURE_TYPES_DO(METASPACE_CLOSURE_TYPE_NAME_CASE)
default:
ShouldNotReachHere();
return nullptr;
}
}
// class MetaspaceClosure::Ref -- // class MetaspaceClosure::Ref --
// //
// MetaspaceClosure can be viewed as a very simple type of copying garbage // For type X to be iterable by MetaspaceClosure, X (or one of X's supertypes) must have
// collector. For it to function properly, it requires each subclass of // the following public functions:
// MetaspaceObj to provide two methods: // void metaspace_pointers_do(MetaspaceClosure* it);
// static bool is_read_only_by_default() { return true; }
// //
// size_t size(); -- to determine how much data to copy // In addition, if X is not a subtype of MetaspaceObj, it must have the following function:
// void metaspace_pointers_do(MetaspaceClosure*); -- to locate all the embedded pointers // MetaspaceClosureType type() const;
// int size_in_heapwords() const;
// //
// Calling these methods would be trivial if these two were virtual methods. // Currently, the iterable types include all subtypes of MetsapceObj, as well
// as GrowableArray, ModuleEntry and PackageEntry.
//
// Calling these functions would be trivial if these were virtual functions.
// However, to save space, MetaspaceObj has NO vtable. The vtable is introduced // However, to save space, MetaspaceObj has NO vtable. The vtable is introduced
// only in the Metadata class. // only in the Metadata class.
// //
@ -97,7 +101,7 @@ public:
// so that we can statically discover the type of a object. The use of Ref // so that we can statically discover the type of a object. The use of Ref
// depends on the fact that: // depends on the fact that:
// //
// [1] We don't use polymorphic pointers for MetaspaceObj's that are not subclasses // [1] We don't use polymorphic pointers to MetaspaceObj's that are not subclasses
// of Metadata. I.e., we don't do this: // of Metadata. I.e., we don't do this:
// class Klass { // class Klass {
// MetaspaceObj *_obj; // MetaspaceObj *_obj;
@ -130,8 +134,7 @@ public:
virtual bool not_null() const = 0; virtual bool not_null() const = 0;
virtual int size() const = 0; virtual int size() const = 0;
virtual void metaspace_pointers_do(MetaspaceClosure *it) 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 MetaspaceClosureType type() const = 0;
virtual MetaspaceObj::Type msotype() const = 0;
virtual bool is_read_only_by_default() const = 0; virtual bool is_read_only_by_default() const = 0;
virtual ~Ref() {} virtual ~Ref() {}
@ -180,7 +183,26 @@ public:
} }
private: private:
// MSORef -- iterate an instance of MetaspaceObj template <typename T, ENABLE_IF(std::is_base_of<MetaspaceObj, T>::value)>
static int get_size(T* obj) {
return obj->size();
}
template <typename T, ENABLE_IF(!std::is_base_of<MetaspaceObj, T>::value)>
static int get_size(T* obj) {
return obj->size_in_heapwords();
}
static MetaspaceClosureType as_type(MetaspaceClosureType t) {
return t;
}
static MetaspaceClosureType as_type(MetaspaceObj::Type msotype) {
precond(msotype < MetaspaceObj::_number_of_types);
return (MetaspaceClosureType)msotype;
}
// MSORef -- iterate an instance of T, where T::metaspace_pointers_do() exists.
template <class T> class MSORef : public Ref { template <class T> class MSORef : public Ref {
T** _mpp; T** _mpp;
T* dereference() const { T* dereference() const {
@ -196,18 +218,20 @@ private:
virtual bool is_read_only_by_default() const { return T::is_read_only_by_default(); } virtual bool is_read_only_by_default() const { return T::is_read_only_by_default(); }
virtual bool not_null() const { return dereference() != nullptr; } virtual bool not_null() const { return dereference() != nullptr; }
virtual int size() const { return dereference()->size(); } virtual int size() const { return get_size(dereference()); }
virtual MetaspaceObj::Type msotype() const { return dereference()->type(); } virtual MetaspaceClosureType type() const { return as_type(dereference()->type()); }
virtual void metaspace_pointers_do(MetaspaceClosure *it) const { virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
dereference()->metaspace_pointers_do(it); 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 //---------------------
// Support for Array<T>
//---------------------
// Abstract base class for MSOArrayRef, MSOPointerArrayRef and OtherArrayRef.
// These are used for iterating Array<T>.
template <class T> class ArrayRef : public Ref { template <class T> class ArrayRef : public Ref {
Array<T>** _mpp; Array<T>** _mpp;
protected: protected:
@ -224,10 +248,10 @@ private:
virtual bool is_read_only_by_default() const { return true; } virtual bool is_read_only_by_default() const { return true; }
virtual bool not_null() const { return dereference() != nullptr; } virtual bool not_null() const { return dereference() != nullptr; }
virtual int size() const { return dereference()->size(); } virtual int size() const { return dereference()->size(); }
virtual MetaspaceObj::Type msotype() const { return MetaspaceObj::array_type(sizeof(T)); } virtual MetaspaceClosureType type() const { return as_type(MetaspaceObj::array_type(sizeof(T))); }
}; };
// OtherArrayRef -- iterate an instance of Array<T>, where T is NOT a subtype of MetaspaceObj. // OtherArrayRef -- iterate an instance of Array<T>, where T does NOT have metaspace_pointer_do().
// T can be a primitive type, such as int, or a structure. However, we do not scan // 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. // the fields inside T, so you should not embed any pointers inside T.
template <class T> class OtherArrayRef : public ArrayRef<T> { template <class T> class OtherArrayRef : public ArrayRef<T> {
@ -238,13 +262,9 @@ private:
Array<T>* array = ArrayRef<T>::dereference(); Array<T>* array = ArrayRef<T>::dereference();
log_trace(aot)("Iter(OtherArray): %p [%d]", array, array->length()); log_trace(aot)("Iter(OtherArray): %p [%d]", array, array->length());
} }
virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
Array<T>* array = (Array<T>*)new_loc;
log_trace(aot)("Iter(OtherArray): %p [%d]", array, array->length());
}
}; };
// MSOArrayRef -- iterate an instance of Array<T>, where T is a subtype of MetaspaceObj. // MSOArrayRef -- iterate an instance of Array<T>, where T has metaspace_pointer_do().
// We recursively call T::metaspace_pointers_do() for each element in this array. // We recursively call T::metaspace_pointers_do() for each element in this array.
template <class T> class MSOArrayRef : public ArrayRef<T> { template <class T> class MSOArrayRef : public ArrayRef<T> {
public: public:
@ -253,9 +273,6 @@ private:
virtual void metaspace_pointers_do(MetaspaceClosure *it) const { virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
metaspace_pointers_do_at_impl(it, ArrayRef<T>::dereference()); metaspace_pointers_do_at_impl(it, ArrayRef<T>::dereference());
} }
virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
metaspace_pointers_do_at_impl(it, (Array<T>*)new_loc);
}
private: private:
void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array<T>* array) const { void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array<T>* array) const {
log_trace(aot)("Iter(MSOArray): %p [%d]", array, array->length()); log_trace(aot)("Iter(MSOArray): %p [%d]", array, array->length());
@ -266,7 +283,7 @@ private:
} }
}; };
// MSOPointerArrayRef -- iterate an instance of Array<T*>, where T is a subtype of MetaspaceObj. // MSOPointerArrayRef -- iterate an instance of Array<T*>, where T has metaspace_pointer_do().
// We recursively call MetaspaceClosure::push() for each pointer in this array. // We recursively call MetaspaceClosure::push() for each pointer in this array.
template <class T> class MSOPointerArrayRef : public ArrayRef<T*> { template <class T> class MSOPointerArrayRef : public ArrayRef<T*> {
public: public:
@ -275,9 +292,6 @@ private:
virtual void metaspace_pointers_do(MetaspaceClosure *it) const { virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
metaspace_pointers_do_at_impl(it, ArrayRef<T*>::dereference()); metaspace_pointers_do_at_impl(it, ArrayRef<T*>::dereference());
} }
virtual void metaspace_pointers_do_at(MetaspaceClosure *it, address new_loc) const {
metaspace_pointers_do_at_impl(it, (Array<T*>*)new_loc);
}
private: private:
void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array<T*>* array) const { void metaspace_pointers_do_at_impl(MetaspaceClosure *it, Array<T*>* array) const {
log_trace(aot)("Iter(MSOPointerArray): %p [%d]", array, array->length()); log_trace(aot)("Iter(MSOPointerArray): %p [%d]", array, array->length());
@ -288,6 +302,102 @@ private:
} }
}; };
//--------------------------------
// Support for AOTGrowableArray<T>
//--------------------------------
// Abstract base class for MSOCArrayRef, MSOPointerCArrayRef and OtherCArrayRef.
// These are used for iterating the buffer held by AOTGrowableArray<T>.
template <class T> class CArrayRef : public Ref {
T** _mpp;
int _num_elems; // Number of elements
int byte_size() const {
return _num_elems * sizeof(T);
}
protected:
// C pointer arrays don't support tagged pointers.
T* dereference() const {
return *_mpp;
}
virtual void** mpp() const {
return (void**)_mpp;
}
int num_elems() const {
return _num_elems;
}
public:
CArrayRef(T** mpp, int num_elems, Writability w)
: Ref(w), _mpp(mpp), _num_elems(num_elems) {
assert(is_aligned(byte_size(), BytesPerWord), "must be");
}
virtual bool is_read_only_by_default() const { return false; }
virtual bool not_null() const { return dereference() != nullptr; }
virtual int size() const { return (int)heap_word_size(byte_size()); }
virtual MetaspaceClosureType type() const { return MetaspaceClosureType::CArrayType; }
};
// OtherCArrayRef -- iterate a C array of type T, where T does NOT have metaspace_pointer_do().
// T can be a primitive type, such as int, or a structure. However, we do not scan
// the fields inside T, so you should not embed any pointers inside T.
template <class T> class OtherCArrayRef : public CArrayRef<T> {
public:
OtherCArrayRef(T** mpp, int num_elems, Writability w) : CArrayRef<T>(mpp, num_elems, w) {}
virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
T* array = CArrayRef<T>::dereference();
log_trace(aot)("Iter(OtherCArray): %p [%d]", array, CArrayRef<T>::num_elems());
}
};
// MSOCArrayRef<T> -- iterate a C array of type T, where T has metaspace_pointer_do().
// We recursively call T::metaspace_pointers_do() for each element in this array.
// This is for supporting AOTGrowableArray<T>.
//
// E.g., PackageEntry* _pkg_entry_pointers[2]; // a buffer that has 2 PackageEntry objects
// ...
// it->push(&_pkg_entry_pointers, 2);
// /* calls _pkg_entry_pointers[0].metaspace_pointers_do(it); */
// /* calls _pkg_entry_pointers[1].metaspace_pointers_do(it); */
template <class T> class MSOCArrayRef : public CArrayRef<T> {
public:
MSOCArrayRef(T** mpp, int num_elems, Writability w) : CArrayRef<T>(mpp, num_elems, w) {}
virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
T* array = CArrayRef<T>::dereference();
log_trace(aot)("Iter(MSOCArray): %p [%d]", array, CArrayRef<T>::num_elems());
for (int i = 0; i < CArrayRef<T>::num_elems(); i++) {
T* elm = array + i;
elm->metaspace_pointers_do(it);
}
}
};
// MSOPointerCArrayRef<T> -- iterate a C array of type T*, where T has metaspace_pointer_do().
// We recursively call MetaspaceClosure::push() for each pointer in this array.
// This is for supporting AOTGrowableArray<T*>.
//
// E.g., PackageEntry** _pkg_entry_pointers[2]; // a buffer that has 2 PackageEntry pointers
// ...
// it->push(&_pkg_entry_pointers, 2);
// /* calls _pkg_entry_pointers[0]->metaspace_pointers_do(it); */
// /* calls _pkg_entry_pointers[1]->metaspace_pointers_do(it); */
template <class T> class MSOPointerCArrayRef : public CArrayRef<T*> {
public:
MSOPointerCArrayRef(T*** mpp, int num_elems, Writability w) : CArrayRef<T*>(mpp, num_elems, w) {}
virtual void metaspace_pointers_do(MetaspaceClosure *it) const {
T** array = CArrayRef<T*>::dereference();
log_trace(aot)("Iter(MSOPointerCArray): %p [%d]", array, CArrayRef<T*>::num_elems());
for (int i = 0; i < CArrayRef<T*>::num_elems(); i++) {
T** mpp = array + i;
it->push(mpp);
}
}
};
// Normally, chains of references like a->b->c->d are iterated recursively. However, // 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 // 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. // MetaspaceClosure::finish(). This avoids overflowing the C stack.
@ -330,37 +440,61 @@ public:
// Array<Array<Klass*>*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef // Array<Array<Klass*>*>* a4 = ...; it->push(&a4); => MSOPointerArrayRef
// Array<Annotation*>* a5 = ...; it->push(&a5); => MSOPointerArrayRef // Array<Annotation*>* a5 = ...; it->push(&a5); => MSOPointerArrayRef
// //
// Note that the following will fail to compile (to prevent you from adding new fields // AOTGrowableArrays have a separate "C array" buffer, so they are scanned in two steps:
// into the MetaspaceObj subtypes that cannot be properly copied by CDS):
// //
// MemoryPool* p = ...; it->push(&p); => MemoryPool is not a subclass of MetaspaceObj // AOTGrowableArray<jlong>* ga1 = ...; it->push(&ga1); => MSORef => OtherCArrayRef
// Array<MemoryPool*>* a6 = ...; it->push(&a6); => MemoryPool is not a subclass of MetaspaceObj // AOTGrowableArray<Annotation>* ga2 = ...; it->push(&ga2); => MSORef => MSOCArrayRef
// Array<int*>* a7 = ...; it->push(&a7); => int is not a subclass of MetaspaceObj // AOTGrowableArray<Klass*>* ga3 = ...; it->push(&ga3); => MSORef => MSOPointerCArrayRef
//
// Note that the following will fail to compile:
//
// MemoryPool* p = ...; it->push(&p); => MemoryPool doesn't have metaspace_pointers_do
// Array<MemoryPool*>* a6 = ...; it->push(&a6); => MemoryPool doesn't have metaspace_pointers_do
// Array<int*>* a7 = ...; it->push(&a7); => int doesn't have metaspace_pointers_do
// --- Regular iterable objects
template <typename T> template <typename T>
void push(T** mpp, Writability w = _default) { void push(T** mpp, Writability w = _default) {
static_assert(std::is_base_of<MetaspaceObj, T>::value, "Do not push pointers of arbitrary types"); static_assert(HAS_METASPACE_POINTERS_DO(T), "Do not push pointers of arbitrary types");
push_with_ref<MSORef<T>>(mpp, w); push_with_ref<MSORef<T>>(mpp, w);
} }
template <typename T, ENABLE_IF(!std::is_base_of<MetaspaceObj, T>::value)> // --- Array<T>
template <typename T, ENABLE_IF(!HAS_METASPACE_POINTERS_DO(T))>
void push(Array<T>** mpp, Writability w = _default) { void push(Array<T>** mpp, Writability w = _default) {
push_with_ref<OtherArrayRef<T>>(mpp, w); push_with_ref<OtherArrayRef<T>>(mpp, w);
} }
template <typename T, ENABLE_IF(std::is_base_of<MetaspaceObj, T>::value)> template <typename T, ENABLE_IF(HAS_METASPACE_POINTERS_DO(T))>
void push(Array<T>** mpp, Writability w = _default) { void push(Array<T>** mpp, Writability w = _default) {
push_with_ref<MSOArrayRef<T>>(mpp, w); push_with_ref<MSOArrayRef<T>>(mpp, w);
} }
template <typename T> template <typename T>
void push(Array<T*>** mpp, Writability w = _default) { void push(Array<T*>** mpp, Writability w = _default) {
static_assert(std::is_base_of<MetaspaceObj, T>::value, "Do not push Arrays of arbitrary pointer types"); static_assert(HAS_METASPACE_POINTERS_DO(T), "Do not push Arrays of arbitrary pointer types");
push_with_ref<MSOPointerArrayRef<T>>(mpp, w); push_with_ref<MSOPointerArrayRef<T>>(mpp, w);
} }
// --- The buffer of AOTGrowableArray<T>
template <typename T, ENABLE_IF(!HAS_METASPACE_POINTERS_DO(T))>
void push_c_array(T** mpp, int num_elems, Writability w = _default) {
push_impl(new OtherCArrayRef<T>(mpp, num_elems, w));
}
template <typename T, ENABLE_IF(HAS_METASPACE_POINTERS_DO(T))>
void push_c_array(T** mpp, int num_elems, Writability w = _default) {
push_impl(new MSOCArrayRef<T>(mpp, num_elems, w));
}
template <typename T>
void push_c_array(T*** mpp, int num_elems, Writability w = _default) {
static_assert(HAS_METASPACE_POINTERS_DO(T), "Do not push C arrays of arbitrary pointer types");
push_impl(new MSOPointerCArrayRef<T>(mpp, num_elems, w));
}
}; };
// This is a special MetaspaceClosure that visits each unique MetaspaceObj once. // This is a special MetaspaceClosure that visits each unique object once.
class UniqueMetaspaceClosure : public MetaspaceClosure { class UniqueMetaspaceClosure : public MetaspaceClosure {
static const int INITIAL_TABLE_SIZE = 15889; static const int INITIAL_TABLE_SIZE = 15889;
static const int MAX_TABLE_SIZE = 1000000; static const int MAX_TABLE_SIZE = 1000000;

View File

@ -0,0 +1,46 @@
/*
* Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_MEMORY_METASPACECLOSURETYPE_HPP
#define SHARE_MEMORY_METASPACECLOSURETYPE_HPP
#include "memory/allocation.hpp"
// MetaspaceClosure is able to iterate on MetaspaceObjs, plus the following classes
#define METASPACE_CLOSURE_TYPES_DO(f) \
METASPACE_OBJ_TYPES_DO(f) \
f(CArray) \
f(GrowableArray) \
f(ModuleEntry) \
f(PackageEntry) \
#define METASPACE_CLOSURE_TYPE_DECLARE(name) name ## Type,
enum class MetaspaceClosureType : int {
METASPACE_CLOSURE_TYPES_DO(METASPACE_CLOSURE_TYPE_DECLARE)
_number_of_types
};
#endif // SHARE_MEMORY_METASPACECLOSURETYPE_HPP

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2000, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -33,6 +33,8 @@
// Array for metadata allocation // Array for metadata allocation
class MetaspaceClosure;
template <typename T> template <typename T>
class Array: public MetaspaceObj { class Array: public MetaspaceObj {
friend class ArchiveBuilder; friend class ArchiveBuilder;
@ -157,6 +159,9 @@ protected:
st->print("Array<T>(" PTR_FORMAT ")", p2i(this)); st->print("Array<T>(" PTR_FORMAT ")", p2i(this));
} }
// This function does nothing. The iteration of the elements are done inside metaspaceClosure.hpp
void metaspace_pointers_do(MetaspaceClosure* it) {}
#ifndef PRODUCT #ifndef PRODUCT
void print(outputStream* st) { void print(outputStream* st) {
for (int i = 0; i< _length; i++) { for (int i = 0; i< _length; i++) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -2693,6 +2693,10 @@ void InstanceKlass::metaspace_pointers_do(MetaspaceClosure* it) {
it->push(&_nest_members); it->push(&_nest_members);
it->push(&_permitted_subclasses); it->push(&_permitted_subclasses);
it->push(&_record_components); it->push(&_record_components);
if (CDSConfig::is_dumping_full_module_graph() && !defined_by_other_loaders()) {
it->push(&_package_entry);
}
} }
#if INCLUDE_CDS #if INCLUDE_CDS
@ -2791,24 +2795,9 @@ void InstanceKlass::remove_java_mirror() {
void InstanceKlass::init_shared_package_entry() { void InstanceKlass::init_shared_package_entry() {
assert(CDSConfig::is_dumping_archive(), "must be"); assert(CDSConfig::is_dumping_archive(), "must be");
#if !INCLUDE_CDS_JAVA_HEAP if (!CDSConfig::is_dumping_full_module_graph() || defined_by_other_loaders()) {
_package_entry = nullptr;
#else
if (CDSConfig::is_dumping_full_module_graph()) {
if (defined_by_other_loaders()) {
_package_entry = nullptr;
} else {
_package_entry = PackageEntry::get_archived_entry(_package_entry);
}
} else if (CDSConfig::is_dumping_dynamic_archive() &&
CDSConfig::is_using_full_module_graph() &&
AOTMetaspace::in_aot_cache(_package_entry)) {
// _package_entry is an archived package in the base archive. Leave it as is.
} else {
_package_entry = nullptr; _package_entry = nullptr;
} }
ArchivePtrMarker::mark_pointer((address**)&_package_entry);
#endif
} }
void InstanceKlass::compute_has_loops_flag_for_methods() { void InstanceKlass::compute_has_loops_flag_for_methods() {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -30,7 +30,6 @@
#include "code/vmreg.hpp" #include "code/vmreg.hpp"
#include "interpreter/linkResolver.hpp" #include "interpreter/linkResolver.hpp"
#include "memory/allStatic.hpp" #include "memory/allStatic.hpp"
#include "memory/metaspaceClosure.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "runtime/safepointVerifiers.hpp" #include "runtime/safepointVerifiers.hpp"
#include "runtime/stubInfo.hpp" #include "runtime/stubInfo.hpp"
@ -38,6 +37,7 @@
class AdapterHandlerEntry; class AdapterHandlerEntry;
class AdapterFingerPrint; class AdapterFingerPrint;
class MetaspaceClosure;
class vframeStream; class vframeStream;
// Runtime is the base class for various runtime interfaces // Runtime is the base class for various runtime interfaces

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 1997, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -116,6 +116,12 @@ protected:
~GrowableArrayView() {} ~GrowableArrayView() {}
protected:
// Used by AOTGrowableArray for MetaspaceClosure support.
E** data_addr() {
return &_data;
}
public: public:
bool operator==(const GrowableArrayView& rhs) const { bool operator==(const GrowableArrayView& rhs) const {
if (_len != rhs._len) if (_len != rhs._len)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2021, 2026, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
@ -21,6 +21,7 @@
* questions. * questions.
*/ */
#include "cds/aotGrowableArray.inline.hpp"
#include "memory/allocation.hpp" #include "memory/allocation.hpp"
#include "memory/metadataFactory.hpp" #include "memory/metadataFactory.hpp"
#include "memory/metaspaceClosure.hpp" #include "memory/metaspaceClosure.hpp"
@ -58,7 +59,7 @@ public:
class MyUniqueMetaspaceClosure : public MetaspaceClosure { class MyUniqueMetaspaceClosure : public MetaspaceClosure {
static constexpr int SIZE = 10; static constexpr int SIZE = 10;
MyMetaData* _visited[SIZE]; void* _visited[SIZE];
int _count; int _count;
public: public:
MyUniqueMetaspaceClosure() { MyUniqueMetaspaceClosure() {
@ -71,11 +72,22 @@ public:
virtual bool do_ref(Ref* ref, bool read_only) { virtual bool do_ref(Ref* ref, bool read_only) {
MyMetaData* ptr = (MyMetaData*)ref->obj(); MyMetaData* ptr = (MyMetaData*)ref->obj();
assert(_count < SIZE, "out of bounds"); assert(_count < SIZE, "out of bounds");
_visited[_count++] = ptr; for (int i = 0; i < _count; i++) {
if (_visited[i] == (void*)ptr) {
// We have walked this before.
return false;
}
}
// Found a new pointer. Let's walk it
_visited[_count++] = (void*)ptr;
return true; // recurse return true; // recurse
} }
bool has_visited(MyMetaData* p) { bool has_visited(MyMetaData* p) {
return has_visited((void*)p);
}
bool has_visited(void* p) {
for (int i = 0; i < SIZE; i++) { for (int i = 0; i < SIZE; i++) {
if (_visited[i] == p) { if (_visited[i] == p) {
return true; return true;
@ -83,6 +95,9 @@ public:
} }
return false; return false;
} }
int visited_count() {
return _count;
}
}; };
// iterate an Array<MyMetaData*> // iterate an Array<MyMetaData*>
@ -104,7 +119,9 @@ TEST_VM(MetaspaceClosure, MSOPointerArrayRef) {
MyUniqueMetaspaceClosure closure; MyUniqueMetaspaceClosure closure;
closure.push(&array); closure.push(&array);
closure.finish();
EXPECT_TRUE(closure.has_visited(array)) << "must be";
EXPECT_TRUE(closure.has_visited(&x)) << "must be"; EXPECT_TRUE(closure.has_visited(&x)) << "must be";
EXPECT_TRUE(closure.has_visited(&y)) << "must be"; EXPECT_TRUE(closure.has_visited(&y)) << "must be";
EXPECT_TRUE(closure.has_visited(&z)) << "must be"; EXPECT_TRUE(closure.has_visited(&z)) << "must be";
@ -130,8 +147,85 @@ TEST_VM(MetaspaceClosure, MSOArrayRef) {
MyUniqueMetaspaceClosure closure; MyUniqueMetaspaceClosure closure;
closure.push(&array); closure.push(&array);
closure.finish();
EXPECT_TRUE(closure.has_visited(array)) << "must be";
EXPECT_TRUE(closure.has_visited(&x)) << "must be"; EXPECT_TRUE(closure.has_visited(&x)) << "must be";
EXPECT_TRUE(closure.has_visited(&y)) << "must be"; EXPECT_TRUE(closure.has_visited(&y)) << "must be";
EXPECT_TRUE(closure.has_visited(&z)) << "must be"; EXPECT_TRUE(closure.has_visited(&z)) << "must be";
} }
// iterate an Array<int>
TEST_VM(MetaspaceClosure, OtherArrayRef) {
JavaThread* THREAD = JavaThread::current();
ClassLoaderData* cld = ClassLoaderData::the_null_class_loader_data();
Array<int>* array = MetadataFactory::new_array<int>(cld, 4, THREAD);
MyUniqueMetaspaceClosure closure;
closure.push(&array);
closure.finish();
EXPECT_TRUE(closure.has_visited(array)) << "must be";
}
// iterate an AOTGrowableArray<MyMetaData*>
TEST_VM(MetaspaceClosure, GrowableArray_MSOPointer) {
AOTGrowableArray<MyMetaData*>* array = new(mtClass) AOTGrowableArray<MyMetaData*>(2, mtClass);
MyMetaData x;
MyMetaData y;
MyMetaData z;
array->push(&x);
array->push(&y);
y._a = &z;
MyUniqueMetaspaceClosure closure;
closure.push(&array);
closure.finish();
EXPECT_TRUE(closure.has_visited(array)) << "must be";
EXPECT_TRUE(closure.has_visited(&x)) << "must be";
EXPECT_TRUE(closure.has_visited(&y)) << "must be";
EXPECT_TRUE(closure.has_visited(&z)) << "must be";
}
// iterate an AOTGrowableArray<MyMetaData>
TEST_VM(MetaspaceClosure, GrowableArray_MSO) {
AOTGrowableArray<MyMetaData>* array = new(mtClass) AOTGrowableArray<MyMetaData>(4, mtClass);
for (int i = 0; i < array->length(); i++) {
EXPECT_TRUE(array->at(i)._a == nullptr) << "should be initialized to null";
EXPECT_TRUE(array->at(i)._b == nullptr) << "should be initialized to null";
}
MyMetaData x;
MyMetaData y;
MyMetaData z;
z._a = &x;
z._b = &y;
y._a = &z;
array->push(z);
MyUniqueMetaspaceClosure closure;
closure.push(&array);
closure.finish();
EXPECT_TRUE(closure.has_visited(array)) << "must be";
EXPECT_TRUE(closure.has_visited(&x)) << "must be";
EXPECT_TRUE(closure.has_visited(&y)) << "must be";
EXPECT_TRUE(closure.has_visited(&z)) << "must be";
}
// iterate an AOTGrowableArray<jlong>
TEST_VM(MetaspaceClosure, GrowableArray_jlong) {
AOTGrowableArray<jlong>* array = new(mtClass) AOTGrowableArray<jlong>(4, mtClass);
MyUniqueMetaspaceClosure closure;
closure.push(&array);
closure.finish();
EXPECT_TRUE(closure.has_visited(array)) << "must be";
EXPECT_TRUE(closure.visited_count() == 2) << "must visit buffer inside GrowableArray";
}