diff --git a/src/hotspot/share/cds/aotClassInitializer.cpp b/src/hotspot/share/cds/aotClassInitializer.cpp index 5b022cae24465..c5ad36ac315bb 100644 --- a/src/hotspot/share/cds/aotClassInitializer.cpp +++ b/src/hotspot/share/cds/aotClassInitializer.cpp @@ -107,7 +107,7 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { // Automatic selection for aot-inited classes // ========================================== // - // When CDSConfig::is_initing_classes_at_dump_time() is enabled, + // When CDSConfig::is_initing_classes_at_dump_time is enabled, // AOTArtifactFinder::find_artifacts() finds the classes of all // heap objects that are reachable from HeapShared::_run_time_special_subgraph, // and mark these classes as aot-inited. This preserves the initialized @@ -266,7 +266,7 @@ bool AOTClassInitializer::can_archive_initialized_mirror(InstanceKlass* ik) { } } - if (CDSConfig::is_dumping_invokedynamic()) { + if (CDSConfig::is_dumping_method_handles()) { // This table was created with the help of CDSHeapVerifier. // Also, some $Holder classes are needed. E.g., Invokers. explicitly // initializes Invokers$Holder. Since Invokers. won't be executed diff --git a/src/hotspot/share/cds/aotClassLinker.cpp b/src/hotspot/share/cds/aotClassLinker.cpp index a1cacd735dd68..dc539eb3d553f 100644 --- a/src/hotspot/share/cds/aotClassLinker.cpp +++ b/src/hotspot/share/cds/aotClassLinker.cpp @@ -142,7 +142,7 @@ bool AOTClassLinker::try_add_candidate(InstanceKlass* ik) { if (ik->is_hidden()) { assert(ik->shared_class_loader_type() != ClassLoader::OTHER, "must have been set"); - if (!CDSConfig::is_dumping_invokedynamic()) { + if (!CDSConfig::is_dumping_method_handles()) { return false; } if (HeapShared::is_lambda_proxy_klass(ik)) { diff --git a/src/hotspot/share/cds/archiveBuilder.cpp b/src/hotspot/share/cds/archiveBuilder.cpp index afd2d909595f5..f8267ea4abf84 100644 --- a/src/hotspot/share/cds/archiveBuilder.cpp +++ b/src/hotspot/share/cds/archiveBuilder.cpp @@ -787,6 +787,7 @@ void ArchiveBuilder::make_klasses_shareable() { const char* aotlinked_msg = ""; const char* inited_msg = ""; Klass* k = get_buffered_addr(klasses()->at(i)); + bool inited = false; k->remove_java_mirror(); #ifdef _LP64 if (UseCompactObjectHeaders) { @@ -811,7 +812,7 @@ void ArchiveBuilder::make_klasses_shareable() { InstanceKlass* ik = InstanceKlass::cast(k); InstanceKlass* src_ik = get_source_addr(ik); bool aotlinked = AOTClassLinker::is_candidate(src_ik); - bool inited = ik->has_aot_initialized_mirror(); + inited = ik->has_aot_initialized_mirror(); ADD_COUNT(num_instance_klasses); if (CDSConfig::is_dumping_dynamic_archive()) { // For static dump, class loader type are already set. @@ -834,7 +835,7 @@ void ArchiveBuilder::make_klasses_shareable() { type = "bad"; assert(0, "shouldn't happen"); } - if (CDSConfig::is_dumping_invokedynamic()) { + if (CDSConfig::is_dumping_method_handles()) { assert(HeapShared::is_archivable_hidden_klass(ik), "sanity"); } else { // Legacy CDS support for lambda proxies @@ -892,7 +893,11 @@ void ArchiveBuilder::make_klasses_shareable() { aotlinked_msg = " aot-linked"; } if (inited) { - inited_msg = " inited"; + if (InstanceKlass::cast(k)->static_field_size() == 0) { + inited_msg = " inited (no static fields)"; + } else { + inited_msg = " inited"; + } } MetaspaceShared::rewrite_nofast_bytecodes_and_calculate_fingerprints(Thread::current(), ik); diff --git a/src/hotspot/share/cds/cdsConfig.cpp b/src/hotspot/share/cds/cdsConfig.cpp index 564298fa5c8e9..9178ea1a9ee5c 100644 --- a/src/hotspot/share/cds/cdsConfig.cpp +++ b/src/hotspot/share/cds/cdsConfig.cpp @@ -45,7 +45,6 @@ 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_archived_invokedynamic = false; bool CDSConfig::_old_cds_flags_used = false; bool CDSConfig::_disable_heap_dumping = false; @@ -656,8 +655,13 @@ bool CDSConfig::is_dumping_invokedynamic() { return AOTInvokeDynamicLinking && is_dumping_aot_linked_classes() && is_dumping_heap(); } -bool CDSConfig::is_loading_invokedynamic() { - return UseSharedSpaces && is_using_full_module_graph() && _has_archived_invokedynamic; +// When we are dumping aot-linked classes and we are able to write archived heap objects, we automatically +// enable the archiving of MethodHandles. This will in turn enable the archiving of MethodTypes and hidden +// classes that are used in the implementation of MethodHandles. +// Archived MethodHandles are required for higher-level optimizations such as AOT resolution of invokedynamic +// and dynamic proxies. +bool CDSConfig::is_dumping_method_handles() { + return is_initing_classes_at_dump_time(); } #endif // INCLUDE_CDS_JAVA_HEAP diff --git a/src/hotspot/share/cds/cdsConfig.hpp b/src/hotspot/share/cds/cdsConfig.hpp index c2dc2b41a93c5..04619f3d07608 100644 --- a/src/hotspot/share/cds/cdsConfig.hpp +++ b/src/hotspot/share/cds/cdsConfig.hpp @@ -39,7 +39,6 @@ class CDSConfig : public AllStatic { static bool _is_dumping_full_module_graph; static bool _is_using_full_module_graph; static bool _has_aot_linked_classes; - static bool _has_archived_invokedynamic; static char* _default_archive_path; static char* _static_archive_path; @@ -126,8 +125,7 @@ class CDSConfig : public AllStatic { static bool is_initing_classes_at_dump_time() NOT_CDS_JAVA_HEAP_RETURN_(false); static bool is_dumping_invokedynamic() NOT_CDS_JAVA_HEAP_RETURN_(false); - static bool is_loading_invokedynamic() NOT_CDS_JAVA_HEAP_RETURN_(false); - static void set_has_archived_invokedynamic() { CDS_JAVA_HEAP_ONLY(_has_archived_invokedynamic = true); } + static bool is_dumping_method_handles() NOT_CDS_JAVA_HEAP_RETURN_(false); // full_module_graph (requires optimized_module_handling) static bool is_dumping_full_module_graph() { return CDS_ONLY(_is_dumping_full_module_graph) NOT_CDS(false); } diff --git a/src/hotspot/share/cds/cdsHeapVerifier.cpp b/src/hotspot/share/cds/cdsHeapVerifier.cpp index f9e613a74cee8..729637f47c246 100644 --- a/src/hotspot/share/cds/cdsHeapVerifier.cpp +++ b/src/hotspot/share/cds/cdsHeapVerifier.cpp @@ -106,6 +106,8 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0) ADD_EXCL("java/lang/System", "bootLayer"); // A + ADD_EXCL("java/util/Collections", "EMPTY_LIST"); // E + // A dummy object used by HashSet. The value doesn't matter and it's never // tested for equality. ADD_EXCL("java/util/HashSet", "PRESENT"); // E @@ -127,7 +129,7 @@ CDSHeapVerifier::CDSHeapVerifier() : _archived_objs(0), _problems(0) ADD_EXCL("sun/invoke/util/ValueConversions", "ONE_INT", // E "ZERO_INT"); // E - if (CDSConfig::is_dumping_invokedynamic()) { + if (CDSConfig::is_dumping_method_handles()) { ADD_EXCL("java/lang/invoke/InvokerBytecodeGenerator", "MEMBERNAME_FACTORY", // D "CD_Object_array", // E same as <...>ConstantUtils.CD_Object_array::CD_Object "INVOKER_SUPER_DESC"); // E same as java.lang.constant.ConstantDescs::CD_Object diff --git a/src/hotspot/share/cds/classListParser.cpp b/src/hotspot/share/cds/classListParser.cpp index e0c008678caf2..133cfcc8738b7 100644 --- a/src/hotspot/share/cds/classListParser.cpp +++ b/src/hotspot/share/cds/classListParser.cpp @@ -627,7 +627,7 @@ void ClassListParser::resolve_indy(JavaThread* current, Symbol* class_name_symbo } void ClassListParser::resolve_indy_impl(Symbol* class_name_symbol, TRAPS) { - if (CDSConfig::is_dumping_invokedynamic()) { + if (CDSConfig::is_dumping_method_handles()) { // The CP entry for the invokedynamic instruction will be resolved. // No need to do the following. return; diff --git a/src/hotspot/share/cds/filemap.cpp b/src/hotspot/share/cds/filemap.cpp index 7db8e78743e20..e9b3b9c862b4c 100644 --- a/src/hotspot/share/cds/filemap.cpp +++ b/src/hotspot/share/cds/filemap.cpp @@ -221,7 +221,6 @@ void FileMapHeader::populate(FileMapInfo *info, size_t core_region_alignment, _use_optimized_module_handling = CDSConfig::is_using_optimized_module_handling(); _has_aot_linked_classes = CDSConfig::is_dumping_aot_linked_classes(); _has_full_module_graph = CDSConfig::is_dumping_full_module_graph(); - _has_archived_invokedynamic = CDSConfig::is_dumping_invokedynamic(); // The following fields are for sanity checks for whether this archive // will function correctly with this JVM and the bootclasspath it's @@ -296,7 +295,6 @@ void FileMapHeader::print(outputStream* st) { st->print_cr("- use_optimized_module_handling: %d", _use_optimized_module_handling); st->print_cr("- has_full_module_graph %d", _has_full_module_graph); st->print_cr("- has_aot_linked_classes %d", _has_aot_linked_classes); - st->print_cr("- has_archived_invokedynamic %d", _has_archived_invokedynamic); } bool FileMapInfo::validate_class_location() { @@ -1885,10 +1883,6 @@ bool FileMapHeader::validate() { if (!_has_full_module_graph) { CDSConfig::stop_using_full_module_graph("archive was created without full module graph"); } - - if (_has_archived_invokedynamic) { - CDSConfig::set_has_archived_invokedynamic(); - } } return true; diff --git a/src/hotspot/share/cds/filemap.hpp b/src/hotspot/share/cds/filemap.hpp index 22f70635e17ca..63166f810ab21 100644 --- a/src/hotspot/share/cds/filemap.hpp +++ b/src/hotspot/share/cds/filemap.hpp @@ -140,7 +140,6 @@ class FileMapHeader: private CDSFileMapHeaderBase { // some expensive operations. bool _has_aot_linked_classes; // Was the CDS archive created with -XX:+AOTClassLinking bool _has_full_module_graph; // Does this CDS archive contain the full archived module graph? - bool _has_archived_invokedynamic; // Does the archive have aot-linked invokedynamic CP entries? HeapRootSegments _heap_root_segments; // Heap root segments info size_t _heap_oopmap_start_pos; // The first bit in the oopmap corresponds to this position in the heap. size_t _heap_ptrmap_start_pos; // The first bit in the ptrmap corresponds to this position in the heap. diff --git a/src/hotspot/share/cds/heapShared.cpp b/src/hotspot/share/cds/heapShared.cpp index 5aa456ea43864..1859b83e08254 100644 --- a/src/hotspot/share/cds/heapShared.cpp +++ b/src/hotspot/share/cds/heapShared.cpp @@ -489,7 +489,7 @@ bool HeapShared::is_string_concat_klass(InstanceKlass* ik) { } bool HeapShared::is_archivable_hidden_klass(InstanceKlass* ik) { - return CDSConfig::is_dumping_invokedynamic() && + return CDSConfig::is_dumping_method_handles() && (is_lambda_form_klass(ik) || is_lambda_proxy_klass(ik) || is_string_concat_klass(ik)); } @@ -779,7 +779,7 @@ void KlassSubGraphInfo::add_subgraph_object_klass(Klass* orig_k) { if (orig_k->is_instance_klass()) { #ifdef ASSERT InstanceKlass* ik = InstanceKlass::cast(orig_k); - if (CDSConfig::is_dumping_invokedynamic()) { + if (CDSConfig::is_dumping_method_handles()) { assert(ik->class_loader() == nullptr || HeapShared::is_lambda_proxy_klass(ik), "we can archive only instances of boot classes or lambda proxy classes"); @@ -832,7 +832,7 @@ void KlassSubGraphInfo::check_allowed_klass(InstanceKlass* ik) { } const char* lambda_msg = ""; - if (CDSConfig::is_dumping_invokedynamic()) { + if (CDSConfig::is_dumping_method_handles()) { lambda_msg = ", or a lambda proxy class"; if (HeapShared::is_lambda_proxy_klass(ik) && (ik->class_loader() == nullptr || @@ -1105,7 +1105,7 @@ void HeapShared::resolve_classes_for_subgraph_of(JavaThread* current, Klass* k) } void HeapShared::initialize_java_lang_invoke(TRAPS) { - if (CDSConfig::is_loading_invokedynamic() || CDSConfig::is_dumping_invokedynamic()) { + if (CDSConfig::is_using_aot_linked_classes() || CDSConfig::is_dumping_method_handles()) { resolve_or_init("java/lang/invoke/Invokers$Holder", true, CHECK); resolve_or_init("java/lang/invoke/MethodHandle", true, CHECK); resolve_or_init("java/lang/invoke/MethodHandleNatives", true, CHECK); diff --git a/src/hotspot/share/cds/lambdaFormInvokers.cpp b/src/hotspot/share/cds/lambdaFormInvokers.cpp index ee3e9cff782ae..bbc4500ab6992 100644 --- a/src/hotspot/share/cds/lambdaFormInvokers.cpp +++ b/src/hotspot/share/cds/lambdaFormInvokers.cpp @@ -95,7 +95,7 @@ void LambdaFormInvokers::regenerate_holder_classes(TRAPS) { return; } - if (CDSConfig::is_dumping_static_archive() && CDSConfig::is_dumping_invokedynamic()) { + if (CDSConfig::is_dumping_static_archive() && CDSConfig::is_dumping_method_handles()) { // Work around JDK-8310831, as some methods in lambda form holder classes may not get generated. log_info(cds)("Archived MethodHandles may refer to lambda form holder classes. Cannot regenerate."); return; diff --git a/src/hotspot/share/cds/metaspaceShared.cpp b/src/hotspot/share/cds/metaspaceShared.cpp index b95d524cb1d35..4b74ded240d42 100644 --- a/src/hotspot/share/cds/metaspaceShared.cpp +++ b/src/hotspot/share/cds/metaspaceShared.cpp @@ -629,7 +629,7 @@ void VM_PopulateDumpSharedSpace::doit() { DEBUG_ONLY(SystemDictionaryShared::NoClassLoadingMark nclm); _pending_method_handle_intrinsics = new (mtClassShared) GrowableArray(256, mtClassShared); - if (CDSConfig::is_dumping_aot_linked_classes()) { + if (CDSConfig::is_dumping_method_handles()) { // When dumping AOT-linked classes, some classes may have direct references to a method handle // intrinsic. The easiest thing is to save all of them into the AOT cache. SystemDictionary::get_all_method_handle_intrinsics(_pending_method_handle_intrinsics); @@ -941,7 +941,7 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS HeapShared::reset_archived_object_states(CHECK); } - if (CDSConfig::is_dumping_invokedynamic()) { + if (CDSConfig::is_dumping_method_handles()) { // This assert means that the MethodType and MethodTypeForm tables won't be // updated concurrently when we are saving their contents into a side table. assert(CDSConfig::allow_only_single_java_thread(), "Required"); @@ -951,12 +951,15 @@ void MetaspaceShared::preload_and_dump_impl(StaticArchiveBuilder& builder, TRAPS vmSymbols::createArchivedObjects(), vmSymbols::void_method_signature(), CHECK); + } + if (CDSConfig::is_initing_classes_at_dump_time()) { // java.lang.Class::reflectionFactory cannot be archived yet. We set this field // to null, and it will be initialized again at runtime. log_debug(cds)("Resetting Class::reflectionFactory"); TempNewSymbol method_name = SymbolTable::new_symbol("resetArchivedStates"); Symbol* method_sig = vmSymbols::void_method_signature(); + JavaValue result(T_VOID); JavaCalls::call_static(&result, vmClasses::Class_klass(), method_name, method_sig, CHECK); diff --git a/src/hotspot/share/classfile/javaClasses.cpp b/src/hotspot/share/classfile/javaClasses.cpp index a224ef481b025..fa97888360a1d 100644 --- a/src/hotspot/share/classfile/javaClasses.cpp +++ b/src/hotspot/share/classfile/javaClasses.cpp @@ -5452,7 +5452,7 @@ void JavaClasses::serialize_offsets(SerializeClosure* soc) { bool JavaClasses::is_supported_for_archiving(oop obj) { Klass* klass = obj->klass(); - if (!CDSConfig::is_dumping_invokedynamic()) { + if (!CDSConfig::is_dumping_method_handles()) { // These are supported by CDS only when CDSConfig::is_dumping_invokedynamic() is enabled. if (klass == vmClasses::ResolvedMethodName_klass() || klass == vmClasses::MemberName_klass()) { diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index b8e24bb2caace..24b6958f4bfd6 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -292,7 +292,7 @@ bool SystemDictionaryShared::check_for_exclusion_impl(InstanceKlass* k) { if (!k->is_hidden() && k->shared_classpath_index() < 0 && is_builtin(k)) { if (k->name()->starts_with("java/lang/invoke/BoundMethodHandle$Species_")) { // This class is dynamically generated by the JDK - if (CDSConfig::is_dumping_aot_linked_classes()) { + if (CDSConfig::is_dumping_method_handles()) { k->set_shared_classpath_index(0); } else { ResourceMark rm; @@ -565,7 +565,7 @@ void SystemDictionaryShared::validate_before_archiving(InstanceKlass* k) { guarantee(!info->is_excluded(), "Should not attempt to archive excluded class %s", name); if (is_builtin(k)) { if (k->is_hidden()) { - if (!CDSConfig::is_dumping_invokedynamic()) { + if (!CDSConfig::is_dumping_method_handles()) { assert(is_registered_lambda_proxy_class(k), "unexpected hidden class %s", name); } } diff --git a/src/hotspot/share/oops/constantPool.cpp b/src/hotspot/share/oops/constantPool.cpp index 8aba2bb2ba354..415ee7426da2e 100644 --- a/src/hotspot/share/oops/constantPool.cpp +++ b/src/hotspot/share/oops/constantPool.cpp @@ -288,7 +288,7 @@ void ConstantPool::klass_at_put(int class_index, Klass* k) { template void ConstantPool::iterate_archivable_resolved_references(Function function) { objArrayOop rr = resolved_references(); - if (rr != nullptr && cache() != nullptr && CDSConfig::is_dumping_invokedynamic()) { + if (rr != nullptr && cache() != nullptr && CDSConfig::is_dumping_method_handles()) { Array* indy_entries = cache()->resolved_indy_entries(); if (indy_entries != nullptr) { for (int i = 0; i < indy_entries->length(); i++) { diff --git a/src/hotspot/share/oops/cpCache.cpp b/src/hotspot/share/oops/cpCache.cpp index f4c53c0089e62..a354e0b07a696 100644 --- a/src/hotspot/share/oops/cpCache.cpp +++ b/src/hotspot/share/oops/cpCache.cpp @@ -572,7 +572,7 @@ bool ConstantPoolCache::can_archive_resolved_method(ConstantPool* src_cp, Resolv method_entry->is_resolved(Bytecodes::_invokespecial)) { return true; } else if (method_entry->is_resolved(Bytecodes::_invokehandle)) { - if (CDSConfig::is_dumping_invokedynamic()) { + if (CDSConfig::is_dumping_method_handles()) { // invokehandle depends on archived MethodType and LambdaForms. return true; } else { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java index 0d75d16d2b04e..af5c619d76793 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/AOTClassLinkingVMOptions.java @@ -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 @@ -80,6 +80,15 @@ public static void main(String[] args) throws Exception { .assertAbnormalExit("CDS archive has aot-linked classes." + " It cannot be used with -Djava.security.manager=default."); + // Dumping with AOTInvokeDynamicLinking disabled + TestCommon.testDump(appJar, TestCommon.list("Hello"), + "-XX:+AOTClassLinking", "-XX:-AOTInvokeDynamicLinking"); + + testCase("Archived full module graph must be enabled at runtime (with -XX:-AOTInvokeDynamicLinking)"); + TestCommon.run("-cp", appJar, "-Djdk.module.validation=1", "Hello") + .assertAbnormalExit("CDS archive has aot-linked classes." + + " It cannot be used when archived full module graph is not used"); + // NOTE: tests for ClassFileLoadHook + AOTClassLinking is in // ../jvmti/ClassFileLoadHookTest.java