mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 03:58:21 +00:00
8350550: Preload classes from AOT cache during VM bootstrap
Reviewed-by: kvn, heidinga, asmehra
This commit is contained in:
parent
61acdf6512
commit
fd30ae988b
@ -23,6 +23,7 @@
|
||||
*/
|
||||
|
||||
#include "cds/aotClassInitializer.hpp"
|
||||
#include "cds/aotLinkedClassBulkLoader.hpp"
|
||||
#include "cds/archiveBuilder.hpp"
|
||||
#include "cds/cdsConfig.hpp"
|
||||
#include "cds/heapShared.hpp"
|
||||
|
||||
@ -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));
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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; }
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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() {
|
||||
|
||||
80
src/hotspot/share/cds/aotOopChecker.cpp
Normal file
80
src/hotspot/share/cds/aotOopChecker.cpp
Normal 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
|
||||
40
src/hotspot/share/cds/aotOopChecker.hpp
Normal file
40
src/hotspot/share/cds/aotOopChecker.hpp
Normal 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
|
||||
@ -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) |
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -234,6 +234,7 @@ class java_lang_String : AllStatic {
|
||||
class java_lang_Class : AllStatic {
|
||||
friend class VMStructs;
|
||||
friend class JVMCIVMStructs;
|
||||
friend class HeapShared;
|
||||
|
||||
private:
|
||||
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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)) {
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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")
|
||||
|
||||
@ -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();
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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.
|
||||
//
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user