Skip to content

8350550: Preload classes from AOT cache during VM bootstrap #26375

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/hotspot/share/cds/aotClassInitializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/

#include "cds/aotClassInitializer.hpp"
#include "cds/aotLinkedClassBulkLoader.hpp"
#include "cds/archiveBuilder.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/heapShared.hpp"
Expand Down Expand Up @@ -349,7 +350,10 @@ bool AOTClassInitializer::is_runtime_setup_required(InstanceKlass* ik) {
ik == vmClasses::internal_Unsafe_klass() ||
ik == vmClasses::ConcurrentHashMap_klass() ||
ik == vmClasses::MethodHandleImpl_klass() ||
ik == vmClasses::Reference_klass();
ik == vmClasses::Reference_klass() ||
ik->name()->equals("java/net/URI") ||
ik->name()->equals("java/net/URL") ||
ik->name()->equals("java/lang/module/ModuleDescriptor");
}

void AOTClassInitializer::call_runtime_setup(JavaThread* current, InstanceKlass* ik) {
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/cds/aotClassLinker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ void AOTClassLinker::write_to_archive() {

if (CDSConfig::is_dumping_aot_linked_classes()) {
AOTLinkedClassTable* table = AOTLinkedClassTable::get(CDSConfig::is_dumping_static_archive());
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));
Expand Down
151 changes: 140 additions & 11 deletions src/hotspot/share/cds/aotLinkedClassBulkLoader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,11 @@
#include "cds/aotLinkedClassBulkLoader.hpp"
#include "cds/aotLinkedClassTable.hpp"
#include "cds/cdsConfig.hpp"
#include "cds/dynamicArchive.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"
Expand All @@ -50,7 +53,7 @@ void AOTLinkedClassBulkLoader::serialize(SerializeClosure* soc, bool is_static_a
AOTLinkedClassTable::get(is_static_archive)->serialize(soc);
}

bool AOTLinkedClassBulkLoader::class_preloading_finished() {
bool AOTLinkedClassBulkLoader::has_finished_loading_classes() {
if (!CDSConfig::is_using_aot_linked_classes()) {
return true;
} else {
Expand All @@ -61,6 +64,123 @@ bool AOTLinkedClassBulkLoader::class_preloading_finished() {
}
}

void AOTLinkedClassBulkLoader::preload_classes(TRAPS) {
log_info(aot, load)("Start preloading 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());

preload_classes_in_table(AOTLinkedClassTable::for_static_archive()->boot1(), "boot1", Handle(), THREAD);
preload_classes_in_table(AOTLinkedClassTable::for_static_archive()->boot2(), "boot2", Handle(), THREAD);
preload_classes_in_table(AOTLinkedClassTable::for_static_archive()->platform(), "plat", h_platform_loader, THREAD);
preload_classes_in_table(AOTLinkedClassTable::for_static_archive()->app(), "app", h_system_loader, THREAD);

log_info(aot, load)("Finished preloading classes");
}

void AOTLinkedClassBulkLoader::preload_classes_in_table(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)("Preload %-5s %s%s", category_name, ik->external_name(),
ik->is_hidden() ? " (hidden)" : "");
}

DEBUG_ONLY({
// The list should be sorted such that ik is placed after all of its supertypes.
precond(!ik->is_loaded());
if (ik->java_super() != nullptr) {
precond(ik->java_super()->is_loaded());
}
for (int i = 0; i < ik->local_interfaces()->length(); i++) {
precond(ik->local_interfaces()->at(i)->is_loaded());
}
});

SystemDictionary::preload_class(loader_data, 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());

validate_module_of_preloaded_classes_in_table(AOTLinkedClassTable::for_static_archive()->boot1(), "boot1", Handle());
validate_module_of_preloaded_classes_in_table(AOTLinkedClassTable::for_static_archive()->boot2(), "boot2", Handle());
validate_module_of_preloaded_classes_in_table(AOTLinkedClassTable::for_static_archive()->platform(), "plat", h_platform_loader);
validate_module_of_preloaded_classes_in_table(AOTLinkedClassTable::for_static_archive()->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

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
Expand All @@ -69,6 +189,8 @@ void AOTLinkedClassBulkLoader::load_javabase_classes(JavaThread* current) {
void AOTLinkedClassBulkLoader::load_non_javabase_classes(JavaThread* current) {
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.
Expand All @@ -84,6 +206,13 @@ void AOTLinkedClassBulkLoader::load_non_javabase_classes(JavaThread* current) {

load_classes_in_loader(current, AOTLinkedClassCategory::APP, SystemDictionary::java_system_loader());

if (CDSConfig::is_using_preloaded_classes()) {
DynamicArchive::setup_and_restore_array_klasses(current);
if (current->has_pending_exception()) {
exit_on_exception(current);
}
}

if (AOTPrintTrainingInfo) {
tty->print_cr("==================== archived_training_data ** after all classes preloaded ====================");
TrainingData::print_archived_training_data_on(tty);
Expand Down Expand Up @@ -159,26 +288,26 @@ void AOTLinkedClassBulkLoader::load_table(AOTLinkedClassTable* table, AOTLinkedC
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);
load_classes_impl(table->boot1(), category_name, loader, CHECK);
break;

case AOTLinkedClassCategory::BOOT2:
load_classes_impl(class_category, table->boot2(), category_name, loader, CHECK);
load_classes_impl(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->boot1());
initiate_loading(THREAD, category_name, loader, table->boot2());
load_classes_impl(class_category, table->platform(), category_name, loader, CHECK);
load_classes_impl(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->boot1());
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);
load_classes_impl(table->app(), category_name, loader, CHECK);
}
break;
case AOTLinkedClassCategory::UNREGISTERED:
Expand All @@ -188,7 +317,7 @@ void AOTLinkedClassBulkLoader::load_table(AOTLinkedClassTable* table, AOTLinkedC
}
}

void AOTLinkedClassBulkLoader::load_classes_impl(AOTLinkedClassCategory class_category, Array<InstanceKlass*>* classes,
void AOTLinkedClassBulkLoader::load_classes_impl(Array<InstanceKlass*>* classes,
const char* category_name, Handle loader, TRAPS) {
if (classes == nullptr) {
return;
Expand Down Expand Up @@ -333,7 +462,7 @@ void AOTLinkedClassBulkLoader::load_hidden_class(ClassLoaderData* loader_data, I
}

void AOTLinkedClassBulkLoader::finish_loading_javabase_classes(TRAPS) {
init_required_classes_for_loader(Handle(), AOTLinkedClassTable::for_static_archive()->boot(), CHECK);
init_required_classes_for_loader(Handle(), AOTLinkedClassTable::for_static_archive()->boot1(), CHECK);
}

// Some AOT-linked classes for <class_loader> must be initialized early. This includes
Expand Down Expand Up @@ -429,9 +558,9 @@ void AOTLinkedClassBulkLoader::replay_training_at_init_for_preloaded_classes(TRA
if (CDSConfig::is_using_aot_linked_classes() && TrainingData::have_data()) {
// Only static archive can have training data.
AOTLinkedClassTable* table = AOTLinkedClassTable::for_static_archive();
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);
}
}
}
18 changes: 15 additions & 3 deletions src/hotspot/share/cds/aotLinkedClassBulkLoader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,25 +47,37 @@ class AOTLinkedClassBulkLoader : AllStatic {
static bool _platform_completed;
static bool _app_completed;
static bool _all_completed;
static bool _preloading_non_javavase_classes;

static void preload_classes_in_table(Array<InstanceKlass*>* classes,
const char* category_name, Handle loader, TRAPS);
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,
static void load_classes_impl(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 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, bool is_static_archive) NOT_CDS_RETURN;

static void preload_classes(TRAPS);
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 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 has_finished_loading_classes() NOT_CDS_RETURN_(true);
static bool is_pending_aot_linked_class(Klass* k) NOT_CDS_RETURN_(false);
};

Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/cds/aotLinkedClassTable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ AOTLinkedClassTable AOTLinkedClassTable::_for_static_archive;
AOTLinkedClassTable AOTLinkedClassTable::_for_dynamic_archive;

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);
Expand Down
12 changes: 6 additions & 6 deletions src/hotspot/share/cds/aotLinkedClassTable.hpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -44,14 +44,14 @@ class AOTLinkedClassTable {
static AOTLinkedClassTable _for_static_archive;
static AOTLinkedClassTable _for_dynamic_archive;

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
Array<InstanceKlass*>* _platform;
Array<InstanceKlass*>* _app;

public:
AOTLinkedClassTable() :
_boot(nullptr), _boot2(nullptr),
_boot1(nullptr), _boot2(nullptr),
_platform(nullptr), _app(nullptr) {}

static AOTLinkedClassTable* for_static_archive() { return &_for_static_archive; }
Expand All @@ -61,12 +61,12 @@ class AOTLinkedClassTable {
return is_static_archive ? for_static_archive() : for_dynamic_archive();
}

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; }
Expand Down
12 changes: 11 additions & 1 deletion src/hotspot/share/cds/cdsConfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ bool CDSConfig::_is_using_optimized_module_handling = true;
bool CDSConfig::_is_dumping_full_module_graph = true;
bool CDSConfig::_is_using_full_module_graph = true;
bool CDSConfig::_has_aot_linked_classes = false;
bool CDSConfig::_has_preloaded_classes = false;
bool CDSConfig::_is_single_command_training = false;
bool CDSConfig::_has_temp_aot_config_file = false;
bool CDSConfig::_old_cds_flags_used = false;
Expand All @@ -65,7 +66,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) |
Expand Down Expand Up @@ -989,6 +991,14 @@ void CDSConfig::set_has_aot_linked_classes(bool has_aot_linked_classes) {
_has_aot_linked_classes |= has_aot_linked_classes;
}

bool CDSConfig::is_using_preloaded_classes() {
return is_using_full_module_graph() && _has_preloaded_classes;
}

void CDSConfig::set_has_preloaded_classes(bool has_preloaded_classes) {
_has_preloaded_classes |= has_preloaded_classes;
}

bool CDSConfig::is_initing_classes_at_dump_time() {
return is_dumping_heap() && is_dumping_aot_linked_classes();
}
Expand Down
Loading