8350550: Preload classes from AOT cache during VM bootstrap

Reviewed-by: kvn, heidinga, asmehra
This commit is contained in:
Ioi Lam 2025-09-23 14:43:16 +00:00
parent 61acdf6512
commit fd30ae988b
43 changed files with 706 additions and 408 deletions

View File

@ -23,6 +23,7 @@
*/
#include "cds/aotClassInitializer.hpp"
#include "cds/aotLinkedClassBulkLoader.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/heapShared.hpp"

View File

@ -192,7 +192,7 @@ void AOTClassLinker::write_to_archive() {
if (CDSConfig::is_dumping_aot_linked_classes()) {
AOTLinkedClassTable* table = AOTLinkedClassTable::get();
table->set_boot(write_classes(nullptr, true));
table->set_boot1(write_classes(nullptr, true));
table->set_boot2(write_classes(nullptr, false));
table->set_platform(write_classes(SystemDictionary::java_platform_loader(), false));
table->set_app(write_classes(SystemDictionary::java_system_loader(), false));

View File

@ -29,6 +29,8 @@
#include "cds/cdsConfig.hpp"
#include "cds/heapShared.hpp"
#include "classfile/classLoaderData.hpp"
#include "classfile/classLoaderDataShared.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/systemDictionary.hpp"
#include "classfile/systemDictionaryShared.hpp"
#include "classfile/vmClasses.hpp"
@ -41,66 +43,197 @@
#include "runtime/handles.inline.hpp"
#include "runtime/java.hpp"
bool AOTLinkedClassBulkLoader::_boot2_completed = false;
bool AOTLinkedClassBulkLoader::_platform_completed = false;
bool AOTLinkedClassBulkLoader::_app_completed = false;
bool AOTLinkedClassBulkLoader::_all_completed = false;
void AOTLinkedClassBulkLoader::serialize(SerializeClosure* soc) {
AOTLinkedClassTable::get()->serialize(soc);
}
bool AOTLinkedClassBulkLoader::class_preloading_finished() {
if (!CDSConfig::is_using_aot_linked_classes()) {
return true;
} else {
// The ConstantPools of preloaded classes have references to other preloaded classes. We don't
// want any Java code (including JVMCI compiler) to use these classes until all of them
// are loaded.
return AtomicAccess::load_acquire(&_all_completed);
// This function is called before the VM executes any Java code (include AOT-compiled Java methods).
//
// We populate the boot/platform/app class loaders with classes from the AOT cache. This is a fundamental
// step in restoring the JVM's state from the snapshot recorded in the AOT cache: other AOT optimizations
// such as AOT compiled methods can make direct references to the preloaded classes, knowing that
// these classes are guaranteed to be in at least the "loaded" state.
void AOTLinkedClassBulkLoader::preload_classes(JavaThread* current) {
preload_classes_impl(current);
if (current->has_pending_exception()) {
exit_on_exception(current);
}
}
void AOTLinkedClassBulkLoader::load_javabase_classes(JavaThread* current) {
assert(CDSConfig::is_using_aot_linked_classes(), "sanity");
load_classes_in_loader(current, AOTLinkedClassCategory::BOOT1, nullptr); // only java.base classes
void AOTLinkedClassBulkLoader::preload_classes_impl(TRAPS) {
precond(CDSConfig::is_using_aot_linked_classes());
ClassLoaderDataShared::restore_archived_modules_for_preloading_classes(THREAD);
Handle h_platform_loader(THREAD, SystemDictionary::java_platform_loader());
Handle h_system_loader(THREAD, SystemDictionary::java_system_loader());
AOTLinkedClassTable* table = AOTLinkedClassTable::get();
preload_classes_in_table(table->boot1(), "boot1", Handle(), CHECK);
preload_classes_in_table(table->boot2(), "boot2", Handle(), CHECK);
initiate_loading(THREAD, "plat", h_platform_loader, table->boot1());
initiate_loading(THREAD, "plat", h_platform_loader, table->boot2());
preload_classes_in_table(table->platform(), "plat", h_platform_loader, CHECK);
initiate_loading(THREAD, "app", h_system_loader, table->boot1());
initiate_loading(THREAD, "app", h_system_loader, table->boot2());
initiate_loading(THREAD, "app", h_system_loader, table->platform());
preload_classes_in_table(table->app(), "app", h_system_loader, CHECK);
}
void AOTLinkedClassBulkLoader::load_non_javabase_classes(JavaThread* current) {
void AOTLinkedClassBulkLoader::preload_classes_in_table(Array<InstanceKlass*>* classes,
const char* category_name, Handle loader, TRAPS) {
if (classes == nullptr) {
return;
}
for (int i = 0; i < classes->length(); i++) {
InstanceKlass* ik = classes->at(i);
if (log_is_enabled(Info, aot, load)) {
ResourceMark rm(THREAD);
log_info(aot, load)("%-5s %s%s", category_name, ik->external_name(),
ik->is_hidden() ? " (hidden)" : "");
}
SystemDictionary::preload_class(loader, ik, CHECK);
if (ik->is_hidden()) {
DEBUG_ONLY({
// Make sure we don't make this hidden class available by name, even if we don't
// use any special ClassLoaderData.
ResourceMark rm(THREAD);
assert(SystemDictionary::find_instance_klass(THREAD, ik->name(), loader) == nullptr,
"hidden classes cannot be accessible by name: %s", ik->external_name());
});
} else {
precond(SystemDictionary::find_instance_klass(THREAD, ik->name(), loader) == ik);
}
}
}
#ifdef ASSERT
void AOTLinkedClassBulkLoader::validate_module_of_preloaded_classes() {
oop javabase_module_oop = ModuleEntryTable::javabase_moduleEntry()->module_oop();
for (int i = T_BOOLEAN; i < T_LONG+1; i++) {
TypeArrayKlass* tak = Universe::typeArrayKlass((BasicType)i);
validate_module(tak, "boot1", javabase_module_oop);
}
JavaThread* current = JavaThread::current();
Handle h_platform_loader(current, SystemDictionary::java_platform_loader());
Handle h_system_loader(current, SystemDictionary::java_system_loader());
AOTLinkedClassTable* table = AOTLinkedClassTable::get();
validate_module_of_preloaded_classes_in_table(table->boot1(), "boot1", Handle());
validate_module_of_preloaded_classes_in_table(table->boot2(), "boot2", Handle());
validate_module_of_preloaded_classes_in_table(table->platform(), "plat", h_platform_loader);
validate_module_of_preloaded_classes_in_table(table->app(), "app", h_system_loader);
}
void AOTLinkedClassBulkLoader::validate_module_of_preloaded_classes_in_table(Array<InstanceKlass*>* classes,
const char* category_name, Handle loader) {
if (classes == nullptr) {
return;
}
ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(loader());
for (int i = 0; i < classes->length(); i++) {
InstanceKlass* ik = classes->at(i);
PackageEntry* pkg_entry = ik->package();
oop module_oop;
if (pkg_entry == nullptr) {
module_oop = loader_data->unnamed_module()->module_oop();
} else {
module_oop = pkg_entry->module()->module_oop();
}
validate_module(ik, category_name, module_oop);
}
}
void AOTLinkedClassBulkLoader::validate_module(Klass* k, const char* category_name, oop module_oop) {
assert(module_oop != nullptr, "module system must have been initialized");
if (log_is_enabled(Debug, aot, module)) {
ResourceMark rm;
log_debug(aot, module)("Validate module of %-5s %s", category_name, k->external_name());
}
precond(java_lang_Class::module(k->java_mirror()) == module_oop);
ArrayKlass* ak = k->array_klass_or_null();
while (ak != nullptr) {
if (log_is_enabled(Debug, aot, module)) {
ResourceMark rm;
log_debug(aot, module)("Validate module of %-5s %s", category_name, ak->external_name());
}
precond(java_lang_Class::module(ak->java_mirror()) == module_oop);
ak = ak->array_klass_or_null();
}
}
#endif
// Link all java.base classes in the AOTLinkedClassTable. Of those classes,
// move the ones that have been AOT-initialized to the "initialized" state.
void AOTLinkedClassBulkLoader::link_or_init_javabase_classes(JavaThread* current) {
link_or_init_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot1(), current);
if (current->has_pending_exception()) {
exit_on_exception(current);
}
}
// Do the same thing as link_or_init_javabase_classes(), but for the classes that are not
// in the java.base module.
void AOTLinkedClassBulkLoader::link_or_init_non_javabase_classes(JavaThread* current) {
link_or_init_non_javabase_classes_impl(current);
if (current->has_pending_exception()) {
exit_on_exception(current);
}
}
void AOTLinkedClassBulkLoader::link_or_init_non_javabase_classes_impl(TRAPS) {
assert(CDSConfig::is_using_aot_linked_classes(), "sanity");
DEBUG_ONLY(validate_module_of_preloaded_classes());
// is_using_aot_linked_classes() requires is_using_full_module_graph(). As a result,
// the platform/system class loader should already have been initialized as part
// of the FMG support.
assert(CDSConfig::is_using_full_module_graph(), "must be");
assert(SystemDictionary::java_platform_loader() != nullptr, "must be");
assert(SystemDictionary::java_system_loader() != nullptr, "must be");
load_classes_in_loader(current, AOTLinkedClassCategory::BOOT2, nullptr); // all boot classes outside of java.base
_boot2_completed = true;
Handle h_platform_loader(THREAD, SystemDictionary::java_platform_loader());
Handle h_system_loader(THREAD, SystemDictionary::java_system_loader());
load_classes_in_loader(current, AOTLinkedClassCategory::PLATFORM, SystemDictionary::java_platform_loader());
_platform_completed = true;
assert(h_platform_loader() != nullptr, "must be");
assert(h_system_loader() != nullptr, "must be");
load_classes_in_loader(current, AOTLinkedClassCategory::APP, SystemDictionary::java_system_loader());
AOTLinkedClassTable* table = AOTLinkedClassTable::get();
link_or_init_classes_for_loader(Handle(), table->boot2(), CHECK);
link_or_init_classes_for_loader(h_platform_loader, table->platform(), CHECK);
link_or_init_classes_for_loader(h_system_loader, table->app(), CHECK);
if (Universe::is_fully_initialized() && VerifyDuringStartup) {
// Make sure we're still in a clean state.
VM_Verify verify_op;
VMThread::execute(&verify_op);
}
if (AOTPrintTrainingInfo) {
tty->print_cr("==================== archived_training_data ** after all classes preloaded ====================");
TrainingData::print_archived_training_data_on(tty);
}
_app_completed = true;
AtomicAccess::release_store(&_all_completed, true);
}
void AOTLinkedClassBulkLoader::load_classes_in_loader(JavaThread* current, AOTLinkedClassCategory class_category, oop class_loader_oop) {
load_classes_in_loader_impl(class_category, class_loader_oop, current);
if (current->has_pending_exception()) {
// We cannot continue, as we might have loaded some of the aot-linked classes, which
// may have dangling C++ pointers to other aot-linked classes that we have failed to load.
exit_on_exception(current);
}
}
// For the AOT cache to function properly, all classes in the AOTLinkedClassTable
// must be loaded and linked. In addition, AOT-initialized classes must be moved to
// the initialized state.
//
// We can encounter a failure during the loading, linking, or initialization of
// classes in the AOTLinkedClassTable only if:
// - We ran out of memory,
// - There is a serious error in the VM implemenation
// When this happens, the VM may be in an inconsistent state (e.g., we have a cached
// heap object of class X, but X is not linked). We must exit the JVM now.
void AOTLinkedClassBulkLoader::exit_on_exception(JavaThread* current) {
assert(current->has_pending_exception(), "precondition");
@ -115,117 +248,6 @@ void AOTLinkedClassBulkLoader::exit_on_exception(JavaThread* current) {
vm_exit_during_initialization("Unexpected exception when loading aot-linked classes.");
}
void AOTLinkedClassBulkLoader::load_classes_in_loader_impl(AOTLinkedClassCategory class_category, oop class_loader_oop, TRAPS) {
Handle h_loader(THREAD, class_loader_oop);
AOTLinkedClassTable* table = AOTLinkedClassTable::get();
load_table(table, class_category, h_loader, CHECK);
// Initialize the InstanceKlasses of all archived heap objects that are reachable from the
// archived java class mirrors.
switch (class_category) {
case AOTLinkedClassCategory::BOOT1:
// Delayed until finish_loading_javabase_classes(), as the VM is not ready to
// execute some of the <clinit> methods.
break;
case AOTLinkedClassCategory::BOOT2:
init_required_classes_for_loader(h_loader, table->boot2(), CHECK);
break;
case AOTLinkedClassCategory::PLATFORM:
init_required_classes_for_loader(h_loader, table->platform(), CHECK);
break;
case AOTLinkedClassCategory::APP:
init_required_classes_for_loader(h_loader, table->app(), CHECK);
break;
case AOTLinkedClassCategory::UNREGISTERED:
ShouldNotReachHere();
break;
}
if (Universe::is_fully_initialized() && VerifyDuringStartup) {
// Make sure we're still in a clean state.
VM_Verify verify_op;
VMThread::execute(&verify_op);
}
}
void AOTLinkedClassBulkLoader::load_table(AOTLinkedClassTable* table, AOTLinkedClassCategory class_category, Handle loader, TRAPS) {
if (class_category != AOTLinkedClassCategory::BOOT1) {
assert(Universe::is_module_initialized(), "sanity");
}
const char* category_name = AOTClassLinker::class_category_name(class_category);
switch (class_category) {
case AOTLinkedClassCategory::BOOT1:
load_classes_impl(class_category, table->boot(), category_name, loader, CHECK);
break;
case AOTLinkedClassCategory::BOOT2:
load_classes_impl(class_category, table->boot2(), category_name, loader, CHECK);
break;
case AOTLinkedClassCategory::PLATFORM:
{
initiate_loading(THREAD, category_name, loader, table->boot());
initiate_loading(THREAD, category_name, loader, table->boot2());
load_classes_impl(class_category, table->platform(), category_name, loader, CHECK);
}
break;
case AOTLinkedClassCategory::APP:
{
initiate_loading(THREAD, category_name, loader, table->boot());
initiate_loading(THREAD, category_name, loader, table->boot2());
initiate_loading(THREAD, category_name, loader, table->platform());
load_classes_impl(class_category, table->app(), category_name, loader, CHECK);
}
break;
case AOTLinkedClassCategory::UNREGISTERED:
default:
ShouldNotReachHere(); // Currently aot-linked classes are not supported for this category.
break;
}
}
void AOTLinkedClassBulkLoader::load_classes_impl(AOTLinkedClassCategory class_category, Array<InstanceKlass*>* classes,
const char* category_name, Handle loader, TRAPS) {
if (classes == nullptr) {
return;
}
ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(loader());
for (int i = 0; i < classes->length(); i++) {
InstanceKlass* ik = classes->at(i);
if (log_is_enabled(Info, aot, load)) {
ResourceMark rm(THREAD);
log_info(aot, load)("%-5s %s%s%s", category_name, ik->external_name(),
ik->is_loaded() ? " (already loaded)" : "",
ik->is_hidden() ? " (hidden)" : "");
}
if (!ik->is_loaded()) {
if (ik->is_hidden()) {
load_hidden_class(loader_data, ik, CHECK);
} else {
InstanceKlass* actual;
if (loader_data == ClassLoaderData::the_null_class_loader_data()) {
actual = SystemDictionary::load_instance_class(ik->name(), loader, CHECK);
} else {
actual = SystemDictionaryShared::find_or_load_shared_class(ik->name(), loader, CHECK);
}
if (actual != ik) {
ResourceMark rm(THREAD);
log_error(aot)("Unable to resolve %s class from %s: %s", category_name, CDSConfig::type_of_archive_being_loaded(), ik->external_name());
log_error(aot)("Expected: " INTPTR_FORMAT ", actual: " INTPTR_FORMAT, p2i(ik), p2i(actual));
log_error(aot)("JVMTI class retransformation is not supported when archive was generated with -XX:+AOTClassLinking.");
AOTMetaspace::unrecoverable_loading_error();
}
assert(actual->is_loaded(), "must be");
}
}
}
}
// Initiate loading of the <classes> in the <initiating_loader>. The <classes> should have already been loaded
// by a parent loader of the <initiating_loader>. This is necessary for handling pre-resolved CP entries.
//
@ -255,7 +277,7 @@ void AOTLinkedClassBulkLoader::initiate_loading(JavaThread* current, const char*
if (log_is_enabled(Info, aot, load)) {
ResourceMark rm(current);
const char* defining_loader = (ik->class_loader() == nullptr ? "boot" : "plat");
log_info(aot, load)("%s %s (initiated, defined by %s)", category_name, ik->external_name(),
log_info(aot, load)("%-5s %s (initiated, defined by %s)", category_name, ik->external_name(),
defining_loader);
}
SystemDictionary::add_to_initiating_loader(current, ik, loader_data);
@ -263,81 +285,11 @@ void AOTLinkedClassBulkLoader::initiate_loading(JavaThread* current, const char*
}
}
// Currently, we archive only three types of hidden classes:
// - LambdaForms
// - lambda proxy classes
// - StringConcat classes
// See HeapShared::is_archivable_hidden_klass().
//
// LambdaForm classes (with names like java/lang/invoke/LambdaForm$MH+0x800000015) logically
// belong to the boot loader, but they are usually stored in their own special ClassLoaderData to
// facilitate class unloading, as a LambdaForm may refer to a class loaded by a custom loader
// that may be unloaded.
//
// We only support AOT-resolution of indys in the boot/platform/app loader, so there's no need
// to support class unloading. For simplicity, we put all archived LambdaForm classes in the
// "main" ClassLoaderData of the boot loader.
//
// (Even if we were to support other loaders, we would still feel free to ignore any requirement
// of class unloading, for any class asset in the AOT cache. Anything that makes it into the AOT
// cache has a lifetime dispensation from unloading. After all, the AOT cache never grows, and
// we can assume that the user is content with its size, and doesn't need its footprint to shrink.)
//
// Lambda proxy classes are normally stored in the same ClassLoaderData as their nest hosts, and
// StringConcat are normally stored in the main ClassLoaderData of the boot class loader. We
// do the same for the archived copies of such classes.
void AOTLinkedClassBulkLoader::load_hidden_class(ClassLoaderData* loader_data, InstanceKlass* ik, TRAPS) {
assert(HeapShared::is_lambda_form_klass(ik) ||
HeapShared::is_lambda_proxy_klass(ik) ||
HeapShared::is_string_concat_klass(ik), "sanity");
DEBUG_ONLY({
assert(ik->super()->is_loaded(), "must be");
for (int i = 0; i < ik->local_interfaces()->length(); i++) {
assert(ik->local_interfaces()->at(i)->is_loaded(), "must be");
}
});
Handle pd;
PackageEntry* pkg_entry = nullptr;
// Since a hidden class does not have a name, it cannot be reloaded
// normally via the system dictionary. Instead, we have to finish the
// loading job here.
if (HeapShared::is_lambda_proxy_klass(ik)) {
InstanceKlass* nest_host = ik->nest_host_not_null();
assert(nest_host->is_loaded(), "must be");
pd = Handle(THREAD, nest_host->protection_domain());
pkg_entry = nest_host->package();
}
ik->restore_unshareable_info(loader_data, pd, pkg_entry, CHECK);
SystemDictionary::load_shared_class_misc(ik, loader_data);
ik->add_to_hierarchy(THREAD);
assert(ik->is_loaded(), "Must be in at least loaded state");
DEBUG_ONLY({
// Make sure we don't make this hidden class available by name, even if we don't
// use any special ClassLoaderData.
Handle loader(THREAD, loader_data->class_loader());
ResourceMark rm(THREAD);
assert(SystemDictionary::resolve_or_null(ik->name(), loader, THREAD) == nullptr,
"hidden classes cannot be accessible by name: %s", ik->external_name());
if (HAS_PENDING_EXCEPTION) {
CLEAR_PENDING_EXCEPTION;
}
});
}
void AOTLinkedClassBulkLoader::finish_loading_javabase_classes(TRAPS) {
init_required_classes_for_loader(Handle(), AOTLinkedClassTable::get()->boot(), CHECK);
}
// Some AOT-linked classes for <class_loader> must be initialized early. This includes
// - classes that were AOT-initialized by AOTClassInitializer
// - the classes of all objects that are reachable from the archived mirrors of
// the AOT-linked classes for <class_loader>.
void AOTLinkedClassBulkLoader::init_required_classes_for_loader(Handle class_loader, Array<InstanceKlass*>* classes, TRAPS) {
void AOTLinkedClassBulkLoader::link_or_init_classes_for_loader(Handle class_loader, Array<InstanceKlass*>* classes, TRAPS) {
if (classes != nullptr) {
for (int i = 0; i < classes->length(); i++) {
InstanceKlass* ik = classes->at(i);
@ -361,56 +313,6 @@ void AOTLinkedClassBulkLoader::init_required_classes_for_loader(Handle class_loa
HeapShared::init_classes_for_special_subgraph(class_loader, CHECK);
}
bool AOTLinkedClassBulkLoader::is_pending_aot_linked_class(Klass* k) {
if (!CDSConfig::is_using_aot_linked_classes()) {
return false;
}
if (_all_completed) { // no more pending aot-linked classes
return false;
}
if (k->is_objArray_klass()) {
k = ObjArrayKlass::cast(k)->bottom_klass();
}
if (!k->is_instance_klass()) {
// type array klasses (and their higher dimensions),
// must have been loaded before a GC can ever happen.
return false;
}
// There's a small window during VM start-up where a not-yet loaded aot-linked
// class k may be discovered by the GC during VM initialization. This can happen
// when the heap contains an aot-cached instance of k, but k is not ready to be
// loaded yet. (TODO: JDK-8342429 eliminates this possibility)
//
// The following checks try to limit this window as much as possible for each of
// the four AOTLinkedClassCategory of classes that can be aot-linked.
InstanceKlass* ik = InstanceKlass::cast(k);
if (ik->defined_by_boot_loader()) {
if (ik->module() != nullptr && ik->in_javabase_module()) {
// AOTLinkedClassCategory::BOOT1 -- all aot-linked classes in
// java.base must have been loaded before a GC can ever happen.
return false;
} else {
// AOTLinkedClassCategory::BOOT2 classes cannot be loaded until
// module system is ready.
return !_boot2_completed;
}
} else if (ik->defined_by_platform_loader()) {
// AOTLinkedClassCategory::PLATFORM classes cannot be loaded until
// the platform class loader is initialized.
return !_platform_completed;
} else if (ik->defined_by_app_loader()) {
// AOTLinkedClassCategory::APP cannot be loaded until the app class loader
// is initialized.
return !_app_completed;
} else {
return false;
}
}
void AOTLinkedClassBulkLoader::replay_training_at_init(Array<InstanceKlass*>* classes, TRAPS) {
if (classes != nullptr) {
for (int i = 0; i < classes->length(); i++) {
@ -425,7 +327,7 @@ void AOTLinkedClassBulkLoader::replay_training_at_init(Array<InstanceKlass*>* cl
void AOTLinkedClassBulkLoader::replay_training_at_init_for_preloaded_classes(TRAPS) {
if (CDSConfig::is_using_aot_linked_classes() && TrainingData::have_data()) {
AOTLinkedClassTable* table = AOTLinkedClassTable::get();
replay_training_at_init(table->boot(), CHECK);
replay_training_at_init(table->boot1(), CHECK);
replay_training_at_init(table->boot2(), CHECK);
replay_training_at_init(table->platform(), CHECK);
replay_training_at_init(table->app(), CHECK);

View File

@ -39,34 +39,40 @@ template <typename T> class Array;
enum class AOTLinkedClassCategory : int;
// During a Production Run, the AOTLinkedClassBulkLoader loads all classes from
// a AOTLinkedClassTable into their respective ClassLoaders. This happens very early
// in the JVM bootstrap stage, before any application code is executed.
// the AOTLinkedClassTable into their respective ClassLoaders. This happens very early
// in the JVM bootstrap stage, before any Java bytecode is executed.
//
// IMPLEMENTATION NOTES:
// We also proactively link all the classes in the AOTLinkedClassTable, and move
// the AOT-initialized classes to the "initialized" state. Due to limitations
// of the current JVM bootstrap sequence, link_or_init_javabase_classes() and
// link_or_init_non_javabase_classes() need to be called after some Java bytecodes are
// executed. Future RFEs will move these calls to earlier stages.
class AOTLinkedClassBulkLoader : AllStatic {
static bool _boot2_completed;
static bool _platform_completed;
static bool _app_completed;
static bool _all_completed;
static void load_classes_in_loader(JavaThread* current, AOTLinkedClassCategory class_category, oop class_loader_oop);
static void load_classes_in_loader_impl(AOTLinkedClassCategory class_category, oop class_loader_oop, TRAPS);
static void load_table(AOTLinkedClassTable* table, AOTLinkedClassCategory class_category, Handle loader, TRAPS);
static void initiate_loading(JavaThread* current, const char* category, Handle initiating_loader, Array<InstanceKlass*>* classes);
static void load_classes_impl(AOTLinkedClassCategory class_category, Array<InstanceKlass*>* classes,
const char* category_name, Handle loader, TRAPS);
static void load_hidden_class(ClassLoaderData* loader_data, InstanceKlass* ik, TRAPS);
static void init_required_classes_for_loader(Handle class_loader, Array<InstanceKlass*>* classes, TRAPS);
static void preload_classes_impl(TRAPS);
static void preload_classes_in_table(Array<InstanceKlass*>* classes,
const char* category_name, Handle loader, TRAPS);
static void initiate_loading(JavaThread* current, const char* category, Handle initiating_loader,
Array<InstanceKlass*>* classes);
static void link_or_init_non_javabase_classes_impl(TRAPS);
static void link_or_init_classes_for_loader(Handle class_loader, Array<InstanceKlass*>* classes, TRAPS);
static void replay_training_at_init(Array<InstanceKlass*>* classes, TRAPS) NOT_CDS_RETURN;
#ifdef ASSERT
static void validate_module_of_preloaded_classes();
static void validate_module_of_preloaded_classes_in_table(Array<InstanceKlass*>* classes,
const char* category_name, Handle loader);
static void validate_module(Klass* k, const char* category_name, oop module_oop);
#endif
public:
static void serialize(SerializeClosure* soc) NOT_CDS_RETURN;
static void load_javabase_classes(JavaThread* current) NOT_CDS_RETURN;
static void load_non_javabase_classes(JavaThread* current) NOT_CDS_RETURN;
static void finish_loading_javabase_classes(TRAPS) NOT_CDS_RETURN;
static void preload_classes(JavaThread* current);
static void link_or_init_javabase_classes(JavaThread* current) NOT_CDS_RETURN;
static void link_or_init_non_javabase_classes(JavaThread* current) NOT_CDS_RETURN;
static void exit_on_exception(JavaThread* current);
static void replay_training_at_init_for_preloaded_classes(TRAPS) NOT_CDS_RETURN;
static bool class_preloading_finished();
static bool is_pending_aot_linked_class(Klass* k) NOT_CDS_RETURN_(false);
};
#endif // SHARE_CDS_AOTLINKEDCLASSBULKLOADER_HPP

View File

@ -30,7 +30,7 @@
AOTLinkedClassTable AOTLinkedClassTable::_instance;
void AOTLinkedClassTable::serialize(SerializeClosure* soc) {
soc->do_ptr((void**)&_boot);
soc->do_ptr((void**)&_boot1);
soc->do_ptr((void**)&_boot2);
soc->do_ptr((void**)&_platform);
soc->do_ptr((void**)&_app);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2024, 2025, 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
@ -41,26 +41,27 @@ class SerializeClosure;
class AOTLinkedClassTable {
static AOTLinkedClassTable _instance;
Array<InstanceKlass*>* _boot; // only java.base classes
Array<InstanceKlass*>* _boot2; // boot classes in other modules
Array<InstanceKlass*>* _boot1; // boot classes in java.base module
Array<InstanceKlass*>* _boot2; // boot classes in all other (named and unnamed) modules,
// including classes from -Xbootclasspath/a
Array<InstanceKlass*>* _platform;
Array<InstanceKlass*>* _app;
public:
AOTLinkedClassTable() :
_boot(nullptr), _boot2(nullptr),
_boot1(nullptr), _boot2(nullptr),
_platform(nullptr), _app(nullptr) {}
static AOTLinkedClassTable* get() {
return &_instance;
}
Array<InstanceKlass*>* boot() const { return _boot; }
Array<InstanceKlass*>* boot1() const { return _boot1; }
Array<InstanceKlass*>* boot2() const { return _boot2; }
Array<InstanceKlass*>* platform() const { return _platform; }
Array<InstanceKlass*>* app() const { return _app; }
void set_boot (Array<InstanceKlass*>* value) { _boot = value; }
void set_boot1 (Array<InstanceKlass*>* value) { _boot1 = value; }
void set_boot2 (Array<InstanceKlass*>* value) { _boot2 = value; }
void set_platform(Array<InstanceKlass*>* value) { _platform = value; }
void set_app (Array<InstanceKlass*>* value) { _app = value; }

View File

@ -124,7 +124,7 @@ bool AOTMetaspace::_use_optimized_module_handling = true;
// These regions are aligned with AOTMetaspace::core_region_alignment().
//
// These 2 regions are populated in the following steps:
// [0] All classes are loaded in AOTMetaspace::preload_classes(). All metadata are
// [0] All classes are loaded in AOTMetaspace::load_classes(). All metadata are
// temporarily allocated outside of the shared regions.
// [1] We enter a safepoint and allocate a buffer for the rw/ro regions.
// [2] C++ vtables are copied into the rw region.
@ -823,12 +823,10 @@ void AOTMetaspace::link_shared_classes(TRAPS) {
}
}
// Preload classes from a list, populate the shared spaces and dump to a
// file.
void AOTMetaspace::preload_and_dump(TRAPS) {
void AOTMetaspace::dump_static_archive(TRAPS) {
CDSConfig::DumperThreadMark dumper_thread_mark(THREAD);
ResourceMark rm(THREAD);
HandleMark hm(THREAD);
HandleMark hm(THREAD);
if (CDSConfig::is_dumping_final_static_archive() && AOTPrintTrainingInfo) {
tty->print_cr("==================== archived_training_data ** before dumping ====================");
@ -836,7 +834,7 @@ void AOTMetaspace::preload_and_dump(TRAPS) {
}
StaticArchiveBuilder builder;
preload_and_dump_impl(builder, THREAD);
dump_static_archive_impl(builder, THREAD);
if (HAS_PENDING_EXCEPTION) {
if (PENDING_EXCEPTION->is_a(vmClasses::OutOfMemoryError_klass())) {
aot_log_error(aot)("Out of memory. Please run with a larger Java heap, current MaxHeapSize = "
@ -902,7 +900,7 @@ void AOTMetaspace::get_default_classlist(char* default_classlist, const size_t b
Arguments::get_java_home(), filesep, filesep);
}
void AOTMetaspace::preload_classes(TRAPS) {
void AOTMetaspace::load_classes(TRAPS) {
char default_classlist[JVM_MAXPATHLEN];
const char* classlist_path;
@ -929,8 +927,8 @@ void AOTMetaspace::preload_classes(TRAPS) {
}
}
// Some classes are used at CDS runtime but are not loaded, and therefore archived, at
// dumptime. We can perform dummmy calls to these classes at dumptime to ensure they
// Some classes are used at CDS runtime but are not yet loaded at this point.
// We can perform dummmy calls to these classes at dumptime to ensure they
// are archived.
exercise_runtime_cds_code(CHECK);
@ -946,10 +944,10 @@ void AOTMetaspace::exercise_runtime_cds_code(TRAPS) {
CDSProtectionDomain::to_file_URL("dummy.jar", Handle(), CHECK);
}
void AOTMetaspace::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS) {
void AOTMetaspace::dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS) {
if (CDSConfig::is_dumping_classic_static_archive()) {
// We are running with -Xshare:dump
preload_classes(CHECK);
load_classes(CHECK);
if (SharedArchiveConfigFile) {
log_info(aot)("Reading extra data from %s ...", SharedArchiveConfigFile);

View File

@ -72,15 +72,15 @@ class AOTMetaspace : AllStatic {
n_regions = 5 // total number of regions
};
static void preload_and_dump(TRAPS) NOT_CDS_RETURN;
static void dump_static_archive(TRAPS) NOT_CDS_RETURN;
#ifdef _LP64
static void adjust_heap_sizes_for_dumping() NOT_CDS_JAVA_HEAP_RETURN;
#endif
private:
static void exercise_runtime_cds_code(TRAPS) NOT_CDS_RETURN;
static void preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS) NOT_CDS_RETURN;
static void preload_classes(TRAPS) NOT_CDS_RETURN;
static void dump_static_archive_impl(StaticArchiveBuilder& builder, TRAPS) NOT_CDS_RETURN;
static void load_classes(TRAPS) NOT_CDS_RETURN;
public:
static Symbol* symbol_rs_base() {

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2025, 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/aotMetaspace.hpp"
#include "cds/aotOopChecker.hpp"
#include "cds/heapShared.hpp"
#include "classfile/javaClasses.hpp"
#include "classfile/symbolTable.hpp"
#include "classfile/vmClasses.hpp"
#include "oops/instanceKlass.inline.hpp"
#include "runtime/fieldDescriptor.inline.hpp"
#include "utilities/debug.hpp"
#if INCLUDE_CDS_JAVA_HEAP
oop AOTOopChecker::get_oop_field(oop obj, const char* name, const char* sig) {
Symbol* name_sym = SymbolTable::probe(name, checked_cast<int>(strlen(name)));
assert(name_sym != nullptr, "Symbol must have been resolved for an existing field of this obj");
Symbol* sig_sym = SymbolTable::probe(sig, checked_cast<int>(strlen(sig)));
assert(sig_sym != nullptr, "Symbol must have been resolved for an existing field of this obj");
fieldDescriptor fd;
Klass* k = InstanceKlass::cast(obj->klass())->find_field(name_sym, sig_sym, &fd);
assert(k != nullptr, "field must exist");
precond(!fd.is_static());
precond(fd.field_type() == T_OBJECT || fd.field_type() == T_ARRAY);
return obj->obj_field(fd.offset());
}
// Make sure we are not caching objects with assumptions that can be violated in
// the production run.
void AOTOopChecker::check(oop obj) {
// Currently we only check URL objects, but more rules may be added in the future.
if (obj->klass()->is_subclass_of(vmClasses::URL_klass())) {
// If URL could be subclassed, obj may have new fields that we don't know about.
precond(vmClasses::URL_klass()->is_final());
// URLs are referenced by the CodeSources/ProtectDomains that are cached
// for AOT-linked classes loaded by the platform/app loaders.
//
// Do not cache any URLs whose URLStreamHandler can be overridden by the application.
// - "jrt" and "file" will always use the built-in URLStreamHandler. See
// java.net.URL::isOverrideable().
// - When an AOT-linked class is loaded from a JAR file, its URL is something
// like file:HelloWorl.jar, and does NOT use the "jar" protocol.
oop protocol = get_oop_field(obj, "protocol", "Ljava/lang/String;");
if (!java_lang_String::equals(protocol, "jrt", 3) &&
!java_lang_String::equals(protocol, "file", 4)) {
ResourceMark rm;
log_error(aot)("Must cache only URLs with jrt/file protocols but got: %s",
java_lang_String::as_quoted_ascii(protocol));
HeapShared::debug_trace();
AOTMetaspace::unrecoverable_writing_error();
}
}
}
#endif //INCLUDE_CDS_JAVA_HEAP

View File

@ -0,0 +1,40 @@
/*
* Copyright (c) 2025, 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_AOTOOPCHECKER_HPP
#define SHARE_CDS_AOTOOPCHECKER_HPP
#include "memory/allStatic.hpp"
#include "oops/oopsHierarchy.hpp"
class AOTOopChecker : AllStatic {
static oop get_oop_field(oop obj, const char* name, const char* sig);
public:
// obj is an object that's about to be stored into the AOT cache. Check if it
// can be safely cached.
static void check(oop obj);
};
#endif // SHARE_CDS_AOTOOPCHECKER_HPP

View File

@ -67,7 +67,8 @@ JavaThread* CDSConfig::_dumper_thread = nullptr;
int CDSConfig::get_status() {
assert(Universe::is_fully_initialized(), "status is finalized only after Universe is initialized");
return (is_dumping_archive() ? IS_DUMPING_ARCHIVE : 0) |
return (is_dumping_aot_linked_classes() ? IS_DUMPING_AOT_LINKED_CLASSES : 0) |
(is_dumping_archive() ? IS_DUMPING_ARCHIVE : 0) |
(is_dumping_method_handles() ? IS_DUMPING_METHOD_HANDLES : 0) |
(is_dumping_static_archive() ? IS_DUMPING_STATIC_ARCHIVE : 0) |
(is_logging_lambda_form_invokers() ? IS_LOGGING_LAMBDA_FORM_INVOKERS : 0) |

View File

@ -80,11 +80,12 @@ class CDSConfig : public AllStatic {
public:
// Used by jdk.internal.misc.CDS.getCDSConfigStatus();
static const int IS_DUMPING_ARCHIVE = 1 << 0;
static const int IS_DUMPING_METHOD_HANDLES = 1 << 1;
static const int IS_DUMPING_STATIC_ARCHIVE = 1 << 2;
static const int IS_LOGGING_LAMBDA_FORM_INVOKERS = 1 << 3;
static const int IS_USING_ARCHIVE = 1 << 4;
static const int IS_DUMPING_AOT_LINKED_CLASSES = 1 << 0;
static const int IS_DUMPING_ARCHIVE = 1 << 1;
static const int IS_DUMPING_METHOD_HANDLES = 1 << 2;
static const int IS_DUMPING_STATIC_ARCHIVE = 1 << 3;
static const int IS_LOGGING_LAMBDA_FORM_INVOKERS = 1 << 4;
static const int IS_USING_ARCHIVE = 1 << 5;
static int get_status() NOT_CDS_RETURN_(0);

View File

@ -40,7 +40,9 @@ bool CDSEnumKlass::is_enum_obj(oop orig_obj) {
InstanceKlass::cast(k)->is_enum_subclass();
}
// -- Handling of Enum objects
// !!! This is legacy support for enum classes before JEP 483. This file is not used when
// !!! CDSConfig::is_initing_classes_at_dump_time()==true.
//
// Java Enum classes have synthetic <clinit> methods that look like this
// enum MyEnum {FOO, BAR}
// MyEnum::<clinint> {
@ -62,6 +64,7 @@ bool CDSEnumKlass::is_enum_obj(oop orig_obj) {
void CDSEnumKlass::handle_enum_obj(int level,
KlassSubGraphInfo* subgraph_info,
oop orig_obj) {
assert(!CDSConfig::is_initing_classes_at_dump_time(), "only for legacy support of enums");
assert(level > 1, "must never be called at the first (outermost) level");
assert(is_enum_obj(orig_obj), "must be");

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2023, 2025, 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
@ -34,6 +34,8 @@ class InstanceKlass;
class JavaFieldStream;
class KlassSubGraphInfo;
// This is legacy support for enum classes before JEP 483. This code is not needed when
// CDSConfig::is_initing_classes_at_dump_time()==true.
class CDSEnumKlass: AllStatic {
public:
static bool is_enum_obj(oop orig_obj);

View File

@ -100,7 +100,7 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
// you might need to fix the core library code, or fix the ADD_EXCL entries below.
//
// class field type
ADD_EXCL("java/lang/ClassLoader", "scl"); // A
ADD_EXCL("java/lang/ClassLoader$Holder", "scl"); // A
ADD_EXCL("java/lang/Module", "ALL_UNNAMED_MODULE", // A
"ALL_UNNAMED_MODULE_SET", // A
"EVERYONE_MODULE", // A
@ -147,6 +147,10 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0)
"ZERO"); // D
}
if (CDSConfig::is_dumping_aot_linked_classes()) {
ADD_EXCL("java/lang/Package$VersionInfo", "NULL_VERSION_INFO"); // D
}
# undef ADD_EXCL
ClassLoaderDataGraph::classes_do(this);
@ -228,10 +232,16 @@ public:
return;
}
if (field_ik == vmClasses::internal_Unsafe_klass() && ArchiveUtils::has_aot_initialized_mirror(field_ik)) {
// There's only a single instance of jdk/internal/misc/Unsafe, so all references will
// be pointing to this singleton, which has been archived.
return;
if (ArchiveUtils::has_aot_initialized_mirror(field_ik)) {
if (field_ik == vmClasses::internal_Unsafe_klass()) {
// There's only a single instance of jdk/internal/misc/Unsafe, so all references will
// be pointing to this singleton, which has been archived.
return;
}
if (field_ik == vmClasses::Boolean_klass()) {
// TODO: check if is TRUE or FALSE
return;
}
}
}

View File

@ -438,8 +438,6 @@ void DynamicArchive::setup_array_klasses() {
if (_dynamic_archive_array_klasses != nullptr) {
for (int i = 0; i < _dynamic_archive_array_klasses->length(); i++) {
ObjArrayKlass* oak = _dynamic_archive_array_klasses->at(i);
assert(!oak->is_typeArray_klass(), "all type array classes must be in static archive");
Klass* elm = oak->element_klass();
assert(AOTMetaspace::in_aot_cache_static_region((void*)elm), "must be");

View File

@ -27,6 +27,7 @@
#include "cds/aotClassLocation.hpp"
#include "cds/aotLogging.hpp"
#include "cds/aotMetaspace.hpp"
#include "cds/aotOopChecker.hpp"
#include "cds/aotReferenceObjSupport.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/archiveHeapLoader.hpp"
@ -325,6 +326,8 @@ bool HeapShared::archive_object(oop obj, oop referrer, KlassSubGraphInfo* subgra
debug_trace();
return false;
} else {
AOTOopChecker::check(obj); // Make sure contents of this oop are safe.
count_allocation(obj->size());
ArchiveHeapWriter::add_source_obj(obj);
CachedOopInfo info = make_cached_oop_info(obj, referrer);
@ -612,7 +615,7 @@ void HeapShared::copy_and_rescan_aot_inited_mirror(InstanceKlass* ik) {
}
}
static void copy_java_mirror_hashcode(oop orig_mirror, oop scratch_m) {
void HeapShared::copy_java_mirror(oop orig_mirror, oop scratch_m) {
// We need to retain the identity_hash, because it may have been used by some hashtables
// in the shared heap.
if (!orig_mirror->fast_no_hash_check()) {
@ -628,6 +631,11 @@ static void copy_java_mirror_hashcode(oop orig_mirror, oop scratch_m) {
DEBUG_ONLY(intptr_t archived_hash = scratch_m->identity_hash());
assert(src_hash == archived_hash, "Different hash codes: original " INTPTR_FORMAT ", archived " INTPTR_FORMAT, src_hash, archived_hash);
}
if (CDSConfig::is_dumping_aot_linked_classes()) {
java_lang_Class::set_module(scratch_m, java_lang_Class::module(orig_mirror));
java_lang_Class::set_protection_domain(scratch_m, java_lang_Class::protection_domain(orig_mirror));
}
}
static objArrayOop get_archived_resolved_references(InstanceKlass* src_ik) {
@ -727,7 +735,7 @@ void HeapShared::write_heap(ArchiveHeapInfo *heap_info) {
void HeapShared::scan_java_mirror(oop orig_mirror) {
oop m = scratch_java_mirror(orig_mirror);
if (m != nullptr) { // nullptr if for custom class loader
copy_java_mirror_hashcode(orig_mirror, m);
copy_java_mirror(orig_mirror, m);
bool success = archive_reachable_objects_from(1, _dump_time_special_subgraph, m);
assert(success, "sanity");
}
@ -1638,9 +1646,11 @@ bool HeapShared::walk_one_object(PendingOopStack* stack, int level, KlassSubGrap
}
if (CDSConfig::is_initing_classes_at_dump_time()) {
// The enum klasses are archived with aot-initialized mirror.
// See AOTClassInitializer::can_archive_initialized_mirror().
// The classes of all archived enum instances have been marked as aot-init,
// so there's nothing else to be done in the production run.
} else {
// This is legacy support for enum classes before JEP 483 -- we cannot rerun
// the enum's <clinit> in the production run, so special handling is needed.
if (CDSEnumKlass::is_enum_obj(orig_obj)) {
CDSEnumKlass::handle_enum_obj(level + 1, subgraph_info, orig_obj);
}

View File

@ -343,6 +343,7 @@ private:
static void prepare_resolved_references();
static void archive_strings();
static void archive_subgraphs();
static void copy_java_mirror(oop orig_mirror, oop scratch_m);
// PendingOop and PendingOopStack are used for recursively discovering all cacheable
// heap objects. The recursion is done using PendingOopStack so we won't overflow the

View File

@ -280,4 +280,17 @@ void ClassLoaderDataShared::restore_java_system_loader_from_archive(ClassLoaderD
_full_module_graph_loaded = true;
}
// This is called before AOTLinkedClassBulkLoader starts preloading classes. It makes sure that
// when we preload any class, its module is already valid.
void ClassLoaderDataShared::restore_archived_modules_for_preloading_classes(JavaThread* current) {
precond(CDSConfig::is_using_aot_linked_classes());
precond(_platform_loader_root_index >= 0);
precond(_system_loader_root_index >= 0);
Handle h_platform_loader(current, HeapShared::get_root(_platform_loader_root_index));
Handle h_system_loader(current, HeapShared::get_root(_system_loader_root_index));
Modules::init_archived_modules(current, h_platform_loader, h_system_loader);
}
#endif // INCLUDE_CDS_JAVA_HEAP

View File

@ -27,6 +27,7 @@
#include "memory/allStatic.hpp"
#include "oops/oopsHierarchy.hpp"
#include "utilities/macros.hpp"
class ClassLoaderData;
class MetaspaceClosure;
@ -37,6 +38,7 @@ class ClassLoaderDataShared : AllStatic {
static bool _full_module_graph_loaded;
CDS_JAVA_HEAP_ONLY(static void ensure_module_entry_table_exists(oop class_loader);)
public:
static void restore_archived_modules_for_preloading_classes(JavaThread* current) NOT_CDS_JAVA_HEAP_RETURN;
#if INCLUDE_CDS_JAVA_HEAP
static void ensure_module_entry_tables_exist();
static void allocate_archived_tables();

View File

@ -1013,12 +1013,25 @@ void java_lang_Class::initialize_mirror_fields(InstanceKlass* ik,
// Set the java.lang.Module module field in the java_lang_Class mirror
void java_lang_Class::set_mirror_module_field(JavaThread* current, Klass* k, Handle mirror, Handle module) {
if (CDSConfig::is_using_aot_linked_classes()) {
oop archived_module = java_lang_Class::module(mirror());
if (archived_module != nullptr) {
precond(module() == nullptr || module() == archived_module);
precond(AOTMetaspace::in_aot_cache_static_region((void*)k));
return;
}
}
if (module.is_null()) {
// During startup, the module may be null only if java.base has not been defined yet.
// Put the class on the fixup_module_list to patch later when the java.lang.Module
// for java.base is known. But note that since we captured the null module another
// thread may have completed that initialization.
// With AOT-linked classes, java.base should have been defined before the
// VM loads any classes.
precond(!CDSConfig::is_using_aot_linked_classes());
bool javabase_was_defined = false;
{
MutexLocker m1(current, Module_lock);
@ -1052,13 +1065,19 @@ void java_lang_Class::set_mirror_module_field(JavaThread* current, Klass* k, Han
// Statically allocate fixup lists because they always get created.
void java_lang_Class::allocate_fixup_lists() {
GrowableArray<Klass*>* mirror_list =
new (mtClass) GrowableArray<Klass*>(40, mtClass);
set_fixup_mirror_list(mirror_list);
if (!CDSConfig::is_using_aot_linked_classes()) {
// fixup_mirror_list() is not used when we have preloaded classes. See
// Universe::fixup_mirrors().
GrowableArray<Klass*>* mirror_list =
new (mtClass) GrowableArray<Klass*>(40, mtClass);
set_fixup_mirror_list(mirror_list);
GrowableArray<Klass*>* module_list =
new (mtModule) GrowableArray<Klass*>(500, mtModule);
set_fixup_module_field_list(module_list);
// With AOT-linked classes, java.base module is defined before any class
// is loaded, so there's no need for fixup_module_field_list().
GrowableArray<Klass*>* module_list =
new (mtModule) GrowableArray<Klass*>(500, mtModule);
set_fixup_module_field_list(module_list);
}
}
void java_lang_Class::allocate_mirror(Klass* k, bool is_scratch, Handle protection_domain, Handle classData,
@ -1158,6 +1177,7 @@ void java_lang_Class::create_mirror(Klass* k, Handle class_loader,
create_scratch_mirror(k, CHECK);
}
} else {
assert(!CDSConfig::is_using_aot_linked_classes(), "should not come here");
assert(fixup_mirror_list() != nullptr, "fixup_mirror_list not initialized");
fixup_mirror_list()->push(k);
}
@ -1203,7 +1223,7 @@ bool java_lang_Class::restore_archived_mirror(Klass *k,
Handle protection_domain, TRAPS) {
// Postpone restoring archived mirror until java.lang.Class is loaded. Please
// see more details in vmClasses::resolve_all().
if (!vmClasses::Class_klass_loaded()) {
if (!vmClasses::Class_klass_loaded() && !CDSConfig::is_using_aot_linked_classes()) {
assert(fixup_mirror_list() != nullptr, "fixup_mirror_list not initialized");
fixup_mirror_list()->push(k);
return true;

View File

@ -234,6 +234,7 @@ class java_lang_String : AllStatic {
class java_lang_Class : AllStatic {
friend class VMStructs;
friend class JVMCIVMStructs;
friend class HeapShared;
private:

View File

@ -697,6 +697,8 @@ void ModuleEntryTable::finalize_javabase(Handle module_handle, Symbol* version,
// classes needing their module field set are added to the fixup_module_list.
// Their module field is set once java.base's java.lang.Module is known to the VM.
void ModuleEntryTable::patch_javabase_entries(JavaThread* current, Handle module_handle) {
assert(!CDSConfig::is_using_aot_linked_classes(), "patching is not necessary with AOT-linked classes");
if (module_handle.is_null()) {
fatal("Unable to patch the module field of classes loaded prior to "
JAVA_BASE_NAME "'s definition, invalid java.lang.Module");

View File

@ -700,6 +700,26 @@ void Modules::serialize_archived_module_info(SerializeClosure* soc) {
void Modules::define_archived_modules(Handle h_platform_loader, Handle h_system_loader, TRAPS) {
assert(CDSConfig::is_using_full_module_graph(), "must be");
if (h_platform_loader.is_null()) {
THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null platform loader object");
}
if (h_system_loader.is_null()) {
THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null system loader object");
}
if (CDSConfig::is_using_aot_linked_classes()) {
// Already initialized
precond(SystemDictionary::java_platform_loader() == h_platform_loader());
precond(SystemDictionary::java_system_loader() == h_system_loader());
} else {
init_archived_modules(THREAD, h_platform_loader, h_system_loader);
}
}
void Modules::init_archived_modules(JavaThread* current, Handle h_platform_loader, Handle h_system_loader) {
assert(CDSConfig::is_using_full_module_graph(), "must be");
ExceptionMark em(current);
// We don't want the classes used by the archived full module graph to be redefined by JVMTI.
// Luckily, such classes are loaded in the JVMTI "early" phase, and CDS is disabled if a JVMTI
@ -708,16 +728,15 @@ void Modules::define_archived_modules(Handle h_platform_loader, Handle h_system_
assert(!(JvmtiExport::should_post_class_file_load_hook() && JvmtiExport::has_early_class_hook_env()),
"CDS should be disabled if early class hooks are enabled");
Handle java_base_module(THREAD, ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data());
// Patch any previously loaded class's module field with java.base's java.lang.Module.
ModuleEntryTable::patch_javabase_entries(THREAD, java_base_module);
if (h_platform_loader.is_null()) {
THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null platform loader object");
if (CDSConfig::is_using_aot_linked_classes()) {
ClassLoaderData* boot_loader_data = ClassLoaderData::the_null_class_loader_data();
ClassLoaderDataShared::archived_boot_unnamed_module()->restore_archived_oops(boot_loader_data);
}
if (h_system_loader.is_null()) {
THROW_MSG(vmSymbols::java_lang_NullPointerException(), "Null system loader object");
Handle java_base_module(current, ClassLoaderDataShared::restore_archived_oops_for_null_class_loader_data());
if (!CDSConfig::is_using_aot_linked_classes()) {
// Patch any previously loaded class's module field with java.base's java.lang.Module.
ModuleEntryTable::patch_javabase_entries(current, java_base_module);
}
ClassLoaderData* platform_loader_data = SystemDictionary::register_loader(h_platform_loader);
@ -777,7 +796,9 @@ void Modules::set_bootloader_unnamed_module(Handle module, TRAPS) {
#if INCLUDE_CDS_JAVA_HEAP
if (CDSConfig::is_using_full_module_graph()) {
precond(unnamed_module == ClassLoaderDataShared::archived_boot_unnamed_module());
unnamed_module->restore_archived_oops(boot_loader_data);
if (!CDSConfig::is_using_aot_linked_classes()) {
unnamed_module->restore_archived_oops(boot_loader_data);
}
} else
#endif
{

View File

@ -57,6 +57,8 @@ public:
static void check_archived_module_oop(oop orig_module_obj) NOT_CDS_JAVA_HEAP_RETURN;
static void define_archived_modules(Handle h_platform_loader, Handle h_system_loader,
TRAPS) NOT_CDS_JAVA_HEAP_RETURN;
static void init_archived_modules(JavaThread* current, Handle h_platform_loader, Handle h_system_loader)
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 serialize_archived_module_info(SerializeClosure* soc) NOT_CDS_JAVA_HEAP_RETURN;

View File

@ -196,14 +196,19 @@ ClassLoaderData* SystemDictionary::register_loader(Handle class_loader, bool cre
}
void SystemDictionary::set_system_loader(ClassLoaderData *cld) {
assert(_java_system_loader.is_empty(), "already set!");
_java_system_loader = cld->class_loader_handle();
if (_java_system_loader.is_empty()) {
_java_system_loader = cld->class_loader_handle();
} else {
assert(_java_system_loader.resolve() == cld->class_loader(), "sanity");
}
}
void SystemDictionary::set_platform_loader(ClassLoaderData *cld) {
assert(_java_platform_loader.is_empty(), "already set!");
_java_platform_loader = cld->class_loader_handle();
if (_java_platform_loader.is_empty()) {
_java_platform_loader = cld->class_loader_handle();
} else {
assert(_java_platform_loader.resolve() == cld->class_loader(), "sanity");
}
}
// ----------------------------------------------------------------------------
@ -1149,6 +1154,58 @@ void SystemDictionary::load_shared_class_misc(InstanceKlass* ik, ClassLoaderData
}
}
// This is much more lightweight than SystemDictionary::resolve_or_null
// - There's only a single Java thread at this point. No need for placeholder.
// - All supertypes of ik have been loaded
// - There's no circularity (checked in AOT assembly phase)
// - There's no need to call java.lang.ClassLoader::load_class() because the boot/platform/app
// loaders are well-behaved
void SystemDictionary::preload_class(Handle class_loader, InstanceKlass* ik, TRAPS) {
precond(Universe::is_bootstrapping());
precond(java_platform_loader() != nullptr && java_system_loader() != nullptr);
precond(class_loader() == nullptr || class_loader() == java_platform_loader() ||class_loader() == java_system_loader());
precond(CDSConfig::is_using_aot_linked_classes());
precond(AOTMetaspace::in_aot_cache_static_region((void*)ik));
precond(!ik->is_loaded());
#ifdef ASSERT
// preload_class() must be called in the correct order -- all super types must have
// already been loaded.
if (ik->java_super() != nullptr) {
assert(ik->java_super()->is_loaded(), "must be");
}
Array<InstanceKlass*>* interfaces = ik->local_interfaces();
int num_interfaces = interfaces->length();
for (int index = 0; index < num_interfaces; index++) {
assert(interfaces->at(index)->is_loaded(), "must be");
}
#endif
ClassLoaderData* loader_data = ClassLoaderData::class_loader_data(class_loader());
oop java_mirror = ik->archived_java_mirror();
precond(java_mirror != nullptr);
Handle pd(THREAD, java_lang_Class::protection_domain(java_mirror));
PackageEntry* pkg_entry = ik->package();
assert(pkg_entry != nullptr || ClassLoader::package_from_class_name(ik->name()) == nullptr,
"non-empty packages must have been archived");
// TODO: the following assert requires JDK-8365580
// assert(is_shared_class_visible(ik->name(), ik, pkg_entry, class_loader), "must be");
ik->restore_unshareable_info(loader_data, pd, pkg_entry, CHECK);
load_shared_class_misc(ik, loader_data);
ik->add_to_hierarchy(THREAD);
if (!ik->is_hidden()) {
update_dictionary(THREAD, ik, loader_data);
}
assert(java_lang_Class::module(java_mirror) != nullptr, "must have been archived");
assert(ik->is_loaded(), "Must be in at least loaded state");
}
#endif // INCLUDE_CDS
InstanceKlass* SystemDictionary::load_instance_class_impl(Symbol* class_name, Handle class_loader, TRAPS) {

View File

@ -326,7 +326,7 @@ private:
static void restore_archived_method_handle_intrinsics_impl(TRAPS) NOT_CDS_RETURN;
protected:
// Used by SystemDictionaryShared and LambdaProxyClassDictionary
// Used by AOTLinkedClassBulkLoader, LambdaProxyClassDictionary, and SystemDictionaryShared
static bool add_loader_constraint(Symbol* name, Klass* klass_being_linked, Handle loader1,
Handle loader2);
@ -337,6 +337,7 @@ protected:
const ClassFileStream *cfs,
PackageEntry* pkg_entry,
TRAPS);
static void preload_class(Handle class_loader, InstanceKlass* ik, TRAPS);
static Handle get_loader_lock_or_null(Handle class_loader);
static InstanceKlass* find_or_define_instance_class(Symbol* class_name,
Handle class_loader,

View File

@ -101,7 +101,11 @@ bool vmClasses::resolve(vmClassID id, TRAPS) {
void vmClasses::resolve_until(vmClassID limit_id, vmClassID &start_id, TRAPS) {
assert((int)start_id <= (int)limit_id, "IDs are out of order!");
for (auto id : EnumRange<vmClassID>{start_id, limit_id}) { // (inclusive start, exclusive end)
resolve(id, CHECK);
if (CDSConfig::is_using_aot_linked_classes()) {
precond(klass_at(id)->is_loaded());
} else {
resolve(id, CHECK);
}
}
// move the starting value forward to the limit:
@ -115,6 +119,10 @@ void vmClasses::resolve_all(TRAPS) {
// after vmSymbols::initialize() is called but before any classes are pre-loaded.
ClassLoader::classLoader_init2(THREAD);
if (CDSConfig::is_using_aot_linked_classes()) {
AOTLinkedClassBulkLoader::preload_classes(THREAD);
}
// Preload commonly used klasses
vmClassID scan = vmClassID::FIRST;
// first do Object, then String, Class
@ -210,9 +218,6 @@ void vmClasses::resolve_all(TRAPS) {
#endif
InstanceStackChunkKlass::init_offset_of_stack();
if (CDSConfig::is_using_aot_linked_classes()) {
AOTLinkedClassBulkLoader::load_javabase_classes(THREAD);
}
}
#if INCLUDE_CDS

View File

@ -852,13 +852,6 @@ nmethod* CompilationPolicy::event(const methodHandle& method, const methodHandle
print_event(bci == InvocationEntryBci ? CALL : LOOP, method(), inlinee(), bci, comp_level);
}
#if INCLUDE_JVMCI
if (EnableJVMCI && UseJVMCICompiler &&
comp_level == CompLevel_full_optimization CDS_ONLY(&& !AOTLinkedClassBulkLoader::class_preloading_finished())) {
return nullptr;
}
#endif
if (comp_level == CompLevel_none &&
JvmtiExport::can_post_interpreter_events() &&
THREAD->is_interp_only_mode()) {
@ -1452,12 +1445,7 @@ CompLevel CompilationPolicy::call_event(const methodHandle& method, CompLevel cu
} else {
next_level = MAX2(osr_level, next_level);
}
#if INCLUDE_JVMCI
if (EnableJVMCI && UseJVMCICompiler &&
next_level == CompLevel_full_optimization CDS_ONLY(&& !AOTLinkedClassBulkLoader::class_preloading_finished())) {
next_level = cur_level;
}
#endif
return next_level;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2014, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2014, 2025, 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
@ -27,7 +27,6 @@
#include "memory/iterator.hpp"
#include "cds/aotLinkedClassBulkLoader.hpp"
#include "classfile/classLoaderData.hpp"
#include "code/nmethod.hpp"
#include "oops/access.inline.hpp"
@ -51,12 +50,7 @@ inline void ClaimMetadataVisitingOopIterateClosure::do_cld(ClassLoaderData* cld)
}
inline void ClaimMetadataVisitingOopIterateClosure::do_klass(Klass* k) {
ClassLoaderData* cld = k->class_loader_data();
if (cld != nullptr) {
ClaimMetadataVisitingOopIterateClosure::do_cld(cld);
} else {
assert(AOTLinkedClassBulkLoader::is_pending_aot_linked_class(k), "sanity");
}
ClaimMetadataVisitingOopIterateClosure::do_cld(k->class_loader_data());
}
inline void ClaimMetadataVisitingOopIterateClosure::do_nmethod(nmethod* nm) {

View File

@ -584,6 +584,11 @@ void Universe::initialize_basic_type_mirrors(TRAPS) {
}
void Universe::fixup_mirrors(TRAPS) {
if (CDSConfig::is_using_aot_linked_classes()) {
// All mirrors of preloaded classes are already restored. No need to fix up.
return;
}
// Bootstrap problem: all classes gets a mirror (java.lang.Class instance) assigned eagerly,
// but we cannot do that for classes created before java.lang.Class is loaded. Here we simply
// walk over permanent objects created so far (mostly classes) and fixup their mirrors. Note

View File

@ -450,7 +450,7 @@ void before_exit(JavaThread* thread, bool halt) {
ClassListWriter::write_resolved_constants();
if (CDSConfig::is_dumping_preimage_static_archive()) {
AOTMetaspace::preload_and_dump(thread);
AOTMetaspace::dump_static_archive(thread);
}
#endif

View File

@ -772,7 +772,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
if (CDSConfig::is_using_aot_linked_classes()) {
SystemDictionary::restore_archived_method_handle_intrinsics();
AOTLinkedClassBulkLoader::finish_loading_javabase_classes(CHECK_JNI_ERR);
AOTLinkedClassBulkLoader::link_or_init_javabase_classes(THREAD);
}
// Start string deduplication thread if requested.
@ -791,7 +791,7 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
call_initPhase2(CHECK_JNI_ERR);
if (CDSConfig::is_using_aot_linked_classes()) {
AOTLinkedClassBulkLoader::load_non_javabase_classes(THREAD);
AOTLinkedClassBulkLoader::link_or_init_non_javabase_classes(THREAD);
}
#ifndef PRODUCT
HeapShared::initialize_test_class_from_archive(THREAD);
@ -889,10 +889,10 @@ jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
if (CDSConfig::is_dumping_classic_static_archive()) {
// Classic -Xshare:dump, aka "old workflow"
AOTMetaspace::preload_and_dump(CHECK_JNI_ERR);
AOTMetaspace::dump_static_archive(CHECK_JNI_ERR);
} else if (CDSConfig::is_dumping_final_static_archive()) {
tty->print_cr("Reading AOTConfiguration %s and writing AOTCache %s", AOTConfiguration, AOTCache);
AOTMetaspace::preload_and_dump(CHECK_JNI_ERR);
AOTMetaspace::dump_static_archive(CHECK_JNI_ERR);
}
if (log_is_enabled(Info, perf, class, link)) {

View File

@ -59,12 +59,15 @@ import jdk.internal.loader.ClassLoaders;
import jdk.internal.loader.NativeLibrary;
import jdk.internal.loader.NativeLibraries;
import jdk.internal.perf.PerfCounter;
import jdk.internal.misc.CDS;
import jdk.internal.misc.Unsafe;
import jdk.internal.misc.VM;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.CallerSensitiveAdapter;
import jdk.internal.reflect.Reflection;
import jdk.internal.util.StaticProperty;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
/**
* A class loader is an object that is responsible for loading classes. The
@ -221,10 +224,16 @@ import jdk.internal.util.StaticProperty;
* @see #resolveClass(Class)
* @since 1.0
*/
@AOTSafeClassInitializer
public abstract class ClassLoader {
private static native void registerNatives();
static {
runtimeSetup();
}
@AOTRuntimeSetup
private static void runtimeSetup() {
registerNatives();
}
@ -1858,8 +1867,8 @@ public abstract class ClassLoader {
throw new IllegalStateException(msg);
default:
// system fully initialized
assert VM.isBooted() && scl != null;
return scl;
assert VM.isBooted() && Holder.scl != null;
return Holder.scl;
}
}
@ -1884,7 +1893,7 @@ public abstract class ClassLoader {
}
// detect recursive initialization
if (scl != null) {
if (Holder.scl != null) {
throw new IllegalStateException("recursive invocation");
}
@ -1895,7 +1904,7 @@ public abstract class ClassLoader {
// custom class loader is only supported to be loaded from unnamed module
Constructor<?> ctor = Class.forName(cn, false, builtinLoader)
.getDeclaredConstructor(ClassLoader.class);
scl = (ClassLoader) ctor.newInstance(builtinLoader);
Holder.scl = (ClassLoader) ctor.newInstance(builtinLoader);
} catch (Exception e) {
Throwable cause = e;
if (e instanceof InvocationTargetException) {
@ -1910,9 +1919,9 @@ public abstract class ClassLoader {
throw new Error(cause.getMessage(), cause);
}
} else {
scl = builtinLoader;
Holder.scl = builtinLoader;
}
return scl;
return Holder.scl;
}
// Returns the class's class loader, or null if none.
@ -1925,9 +1934,13 @@ public abstract class ClassLoader {
return caller.getClassLoader0();
}
// The system class loader
// @GuardedBy("ClassLoader.class")
private static volatile ClassLoader scl;
// Holder has the field(s) that need to be initialized during JVM bootstrap even if
// the outer is aot-initialized.
private static class Holder {
// The system class loader
// @GuardedBy("ClassLoader.class")
private static volatile ClassLoader scl;
}
// -- Package --
@ -2602,7 +2615,21 @@ public abstract class ClassLoader {
if (parallelLockMap != null) {
reinitObjectField("parallelLockMap", new ConcurrentHashMap<>());
}
reinitObjectField("packages", new ConcurrentHashMap<>());
if (CDS.isDumpingAOTLinkedClasses()) {
if (System.getProperty("cds.debug.archived.packages") != null) {
for (Map.Entry<String, NamedPackage> entry : packages.entrySet()) {
String key = entry.getKey();
NamedPackage value = entry.getValue();
System.out.println("Archiving " +
(value instanceof Package ? "Package" : "NamedPackage") +
" \"" + key + "\" for " + this);
}
}
} else {
reinitObjectField("packages", new ConcurrentHashMap<>());
}
reinitObjectField("package2certs", new ConcurrentHashMap<>());
classes.clear();
classes.trimToSize();

View File

@ -54,6 +54,8 @@ import static java.util.Objects.*;
import jdk.internal.module.Checks;
import jdk.internal.module.ModuleInfo;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
/**
@ -91,6 +93,7 @@ import jdk.internal.module.ModuleInfo;
* @since 9
*/
@AOTSafeClassInitializer
public final class ModuleDescriptor
implements Comparable<ModuleDescriptor>
{
@ -2665,6 +2668,11 @@ public final class ModuleDescriptor
}
static {
runtimeSetup();
}
@AOTRuntimeSetup
private static void runtimeSetup() {
/**
* Setup the shared secret to allow code in other packages access
* private package methods in java.lang.module.

View File

@ -43,6 +43,8 @@ import java.text.Normalizer;
import jdk.internal.access.JavaNetUriAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.util.Exceptions;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import sun.nio.cs.UTF_8;
import static jdk.internal.util.Exceptions.filterNonSocketInfo;
@ -516,6 +518,7 @@ import static jdk.internal.util.Exceptions.formatMsg;
* @see <a href="URISyntaxException.html">URISyntaxException</a>
*/
@AOTSafeClassInitializer
public final class URI
implements Comparable<URI>, Serializable
{
@ -3726,7 +3729,13 @@ public final class URI
}
}
static {
runtimeSetup();
}
@AOTRuntimeSetup
private static void runtimeSetup() {
SharedSecrets.setJavaNetUriAccess(
new JavaNetUriAccess() {
public URI create(String scheme, String path) {

View File

@ -43,6 +43,8 @@ import jdk.internal.access.JavaNetURLAccess;
import jdk.internal.access.SharedSecrets;
import jdk.internal.misc.ThreadTracker;
import jdk.internal.misc.VM;
import jdk.internal.vm.annotation.AOTRuntimeSetup;
import jdk.internal.vm.annotation.AOTSafeClassInitializer;
import sun.net.util.IPAddressUtil;
import static jdk.internal.util.Exceptions.filterNonSocketInfo;
import static jdk.internal.util.Exceptions.formatMsg;
@ -214,6 +216,7 @@ import static jdk.internal.util.Exceptions.formatMsg;
* @author James Gosling
* @since 1.0
*/
@AOTSafeClassInitializer
public final class URL implements java.io.Serializable {
static final String BUILTIN_HANDLERS_PREFIX = "sun.net.www.protocol";
@ -1758,6 +1761,11 @@ public final class URL implements java.io.Serializable {
}
static {
runtimeSetup();
}
@AOTRuntimeSetup
private static void runtimeSetup() {
SharedSecrets.setJavaNetURLAccess(
new JavaNetURLAccess() {
@Override

View File

@ -29,6 +29,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import jdk.internal.misc.CDS;
/**
* This class extends {@code ClassLoader} with additional support for defining
@ -243,6 +244,20 @@ public class SecureClassLoader extends ClassLoader {
* Called by the VM, during -Xshare:dump
*/
private void resetArchivedStates() {
pdcache.clear();
if (CDS.isDumpingAOTLinkedClasses()) {
for (CodeSourceKey key : pdcache.keySet()) {
if (key.cs.getCodeSigners() != null) {
// We don't archive any signed classes, so we don't need to cache their ProtectionDomains.
pdcache.remove(key);
}
}
if (System.getProperty("cds.debug.archived.protection.domains") != null) {
for (CodeSourceKey key : pdcache.keySet()) {
System.out.println("Archiving ProtectionDomain " + key.cs + " for " + this);
}
}
} else {
pdcache.clear();
}
}
}

View File

@ -75,9 +75,13 @@ public class BootLoader {
private static final ConcurrentHashMap<?, ?> CLASS_LOADER_VALUE_MAP
= new ConcurrentHashMap<>();
// native libraries loaded by the boot class loader
private static final NativeLibraries NATIVE_LIBS
= NativeLibraries.newInstance(null);
// Holder has the field(s) that need to be initialized during JVM bootstrap even if
// the outer is aot-initialized.
private static class Holder {
// native libraries loaded by the boot class loader
private static final NativeLibraries NATIVE_LIBS
= NativeLibraries.newInstance(null);
}
/**
* Returns the unnamed module for the boot loader.
@ -104,7 +108,7 @@ public class BootLoader {
* Returns NativeLibraries for the boot class loader.
*/
public static NativeLibraries getNativeLibraries() {
return NATIVE_LIBS;
return Holder.NATIVE_LIBS;
}
/**

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, 2025, 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
@ -153,7 +153,7 @@ public final class NativeLibraries {
}
// cannot be loaded by other class loaders
if (loadedLibraryNames.contains(name)) {
if (Holder.loadedLibraryNames.contains(name)) {
throw new UnsatisfiedLinkError("Native Library " + name +
" already loaded in another classloader");
}
@ -203,7 +203,7 @@ public final class NativeLibraries {
NativeLibraryContext.pop();
}
// register the loaded native library
loadedLibraryNames.add(name);
Holder.loadedLibraryNames.add(name);
libraries.put(name, lib);
return lib;
} finally {
@ -243,6 +243,11 @@ public final class NativeLibraries {
return lib;
}
// Called at the end of AOTCache assembly phase.
public void clear() {
libraries.clear();
}
private NativeLibrary findFromPaths(String[] paths, Class<?> fromClass, String name) {
for (String path : paths) {
File libfile = new File(path, System.mapLibraryName(name));
@ -368,7 +373,7 @@ public final class NativeLibraries {
acquireNativeLibraryLock(name);
try {
/* remove the native library name */
if (!loadedLibraryNames.remove(name)) {
if (!Holder.loadedLibraryNames.remove(name)) {
throw new IllegalStateException(name + " has already been unloaded");
}
NativeLibraryContext.push(UNLOADER);
@ -395,9 +400,13 @@ public final class NativeLibraries {
static final String[] USER_PATHS = ClassLoaderHelper.parsePath(StaticProperty.javaLibraryPath());
}
// All native libraries we've loaded.
private static final Set<String> loadedLibraryNames =
// Holder has the fields that need to be initialized during JVM bootstrap even if
// the outer is aot-initialized.
static class Holder {
// All native libraries we've loaded.
private static final Set<String> loadedLibraryNames =
ConcurrentHashMap.newKeySet();
}
// reentrant lock class that allows exact counting (with external synchronization)
@SuppressWarnings("serial")

View File

@ -47,11 +47,13 @@ import jdk.internal.util.StaticProperty;
public class CDS {
// Must be in sync with cdsConfig.hpp
private static final int IS_DUMPING_ARCHIVE = 1 << 0;
private static final int IS_DUMPING_METHOD_HANDLES = 1 << 1;
private static final int IS_DUMPING_STATIC_ARCHIVE = 1 << 2;
private static final int IS_LOGGING_LAMBDA_FORM_INVOKERS = 1 << 3;
private static final int IS_USING_ARCHIVE = 1 << 4;
private static final int IS_DUMPING_AOT_LINKED_CLASSES = 1 << 0;
private static final int IS_DUMPING_ARCHIVE = 1 << 1;
private static final int IS_DUMPING_METHOD_HANDLES = 1 << 2;
private static final int IS_DUMPING_STATIC_ARCHIVE = 1 << 3;
private static final int IS_LOGGING_LAMBDA_FORM_INVOKERS = 1 << 4;
private static final int IS_USING_ARCHIVE = 1 << 5;
private static final int configStatus = getCDSConfigStatus();
/**
@ -82,6 +84,10 @@ public class CDS {
return (configStatus & IS_DUMPING_STATIC_ARCHIVE) != 0;
}
public static boolean isDumpingAOTLinkedClasses() {
return (configStatus & IS_DUMPING_AOT_LINKED_CLASSES) != 0;
}
public static boolean isSingleThreadVM() {
return isDumpingStaticArchive();
}

View File

@ -17,3 +17,7 @@ compiler/intrinsics/klass/TestIsPrimitive.java 0000000 generic-all
# It has the assumption about unresolved Integer.
# However when AOTClassLinking is enabled, Integer is always resolved at JVM start-up.
compiler/ciReplay/TestInliningProtectionDomain.java 0000000 generic-all
# These tests fail often with AotJdk due to JDK-8323727
compiler/arguments/TestStressReflectiveCode.java 8323727 generic-all
compiler/arraycopy/TestCloneWithStressReflectiveCode.java 8323727 generic-all

View File

@ -33,6 +33,7 @@
* MethodHandleTestApp MethodHandleTestApp$A MethodHandleTestApp$B
* UnsupportedBSMs UnsupportedBSMs$MyEnum
* ObjectMethodsTest ObjectMethodsTest$C
* InterfaceWithEnum EnumWithClinit
* @run driver MethodHandleTest AOT --two-step-training
*/
@ -101,6 +102,11 @@ public class MethodHandleTest {
out.shouldNotContain("MethodHandleTestApp.<clinit>");
out.shouldContain("intElm = 777");
}
// For MethodHandleTestApp.testLambdaWithEnums()
if (runMode == RunMode.ASSEMBLY) {
out.shouldNotContain("EnumWithClinit.<clinit>");
}
}
}
}
@ -206,6 +212,8 @@ class MethodHandleTestApp {
ObjectMethodsTest.testEqualsC(ObjectMethodsTest_handle);
testLambdaWithEnums();
UnsupportedBSMs.invokeUnsupportedBSMs();
}
@ -275,6 +283,29 @@ class MethodHandleTestApp {
}
}
}
static boolean InterfaceWithEnum_inited = false;
// Enum types used in lambdas shouldn't be initialized during the assembly phase.
static void testLambdaWithEnums() {
if (InterfaceWithEnum_inited) {
throw new RuntimeException("InterfaceWithEnum should not be inited");
}
InterfaceWithEnum iwe = (x) -> {
System.out.println("Hello from testLambdaWithEnums");
};
System.out.println(iwe);
if (InterfaceWithEnum_inited) {
throw new RuntimeException("InterfaceWithEnum should not be inited");
}
iwe.func(EnumWithClinit.Dummy);
if (!InterfaceWithEnum_inited) {
throw new RuntimeException("InterfaceWithEnum should be inited");
}
}
}
// Excerpt from test/jdk/java/lang/runtime/ObjectMethodsTest.java
@ -332,6 +363,18 @@ class ObjectMethodsTest {
}
}
interface InterfaceWithEnum {
void func(EnumWithClinit e);
}
enum EnumWithClinit {
Dummy;
static {
MethodHandleTestApp.InterfaceWithEnum_inited = true;
System.out.println("EnumWithClinit.<clinit>");
}
}
class UnsupportedBSMs {
// This method is executed during the assembly phase.
//