diff --git a/src/hotspot/share/cds/aotArtifactFinder.cpp b/src/hotspot/share/cds/aotArtifactFinder.cpp index e644a5e6f5fd1..f39aac24fed89 100644 --- a/src/hotspot/share/cds/aotArtifactFinder.cpp +++ b/src/hotspot/share/cds/aotArtifactFinder.cpp @@ -149,10 +149,11 @@ void AOTArtifactFinder::find_artifacts() { SystemDictionaryShared::dumptime_table()->iterate_all_live_classes([&] (InstanceKlass* k, DumpTimeClassInfo& info) { if (!info.is_excluded() && _seen_classes->get(k) == nullptr) { info.set_excluded(); + info.set_has_checked_exclusion(); assert(k->is_hidden(), "must be"); if (log_is_enabled(Info, cds)) { ResourceMark rm; - log_info(cds)("Skipping %s: Hidden class", k->name()->as_C_string()); + log_debug(cds)("Skipping %s: Unreferenced hidden class", k->name()->as_C_string()); } } }); @@ -208,6 +209,10 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) { _seen_classes->put_if_absent(ik, &created); if (created) { _all_cached_classes->append(ik); + if (CDSConfig::is_dumping_final_static_archive() && ik->is_shared_unregistered_class()) { + // The following are not appliable to unregistered classes + return; + } scan_oops_in_instance_class(ik); if (ik->is_hidden() && CDSConfig::is_initing_classes_at_dump_time()) { bool succeed = AOTClassLinker::try_add_candidate(ik); diff --git a/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp b/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp index 042a0dadb201f..6f5ca6d791567 100644 --- a/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp +++ b/src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp @@ -1,6 +1,6 @@ /* - * Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2021, 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 @@ -28,6 +28,7 @@ #include "cds/dumpTimeClassInfo.hpp" +#include "cds/cdsConfig.hpp" #include "classfile/systemDictionaryShared.hpp" #include "classfile/classLoaderData.inline.hpp" #include "oops/instanceKlass.hpp" @@ -44,7 +45,10 @@ void DumpTimeSharedClassTable::iterate_all_live_classes(Function function) const auto wrapper = [&] (InstanceKlass* k, DumpTimeClassInfo& info) { assert(SafepointSynchronize::is_at_safepoint(), "invariant"); assert_lock_strong(DumpTimeTable_lock); - if (k->is_loader_alive()) { + if (CDSConfig::is_dumping_final_static_archive() && !k->is_loaded()) { + assert(k->is_shared_unregistered_class(), "must be"); + function(k, info); + } else if (k->is_loader_alive()) { function(k, info); assert(k->is_loader_alive(), "must not change"); } else { diff --git a/src/hotspot/share/cds/finalImageRecipes.cpp b/src/hotspot/share/cds/finalImageRecipes.cpp index 55855679a1cfa..674a44937eb1f 100644 --- a/src/hotspot/share/cds/finalImageRecipes.cpp +++ b/src/hotspot/share/cds/finalImageRecipes.cpp @@ -102,7 +102,10 @@ void FinalImageRecipes::load_all_classes(TRAPS) { Klass* k = _all_klasses->at(i); if (k->is_instance_klass()) { InstanceKlass* ik = InstanceKlass::cast(k); - if (!ik->is_shared_unregistered_class() && !ik->is_hidden()) { + if (ik->is_shared_unregistered_class()) { + SystemDictionaryShared::init_dumptime_info(ik); + SystemDictionaryShared::add_unregistered_class(THREAD, ik); + } else if (!ik->is_hidden()) { Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), class_loader, true, CHECK); if (actual != ik) { ResourceMark rm(THREAD); diff --git a/src/hotspot/share/classfile/systemDictionaryShared.cpp b/src/hotspot/share/classfile/systemDictionaryShared.cpp index c4e061b8fa993..78aafc2fe6487 100644 --- a/src/hotspot/share/classfile/systemDictionaryShared.cpp +++ b/src/hotspot/share/classfile/systemDictionaryShared.cpp @@ -662,7 +662,7 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) { } void SystemDictionaryShared::finish_exclusion_checks() { - if (CDSConfig::is_dumping_dynamic_archive()) { + if (CDSConfig::is_dumping_dynamic_archive() || CDSConfig::is_dumping_preimage_static_archive()) { // Do this first -- if a base class is excluded due to duplication, // all of its subclasses will also be excluded. ResourceMark rm; diff --git a/src/hotspot/share/oops/klass.cpp b/src/hotspot/share/oops/klass.cpp index 9f166e13751a7..71da5e1da095b 100644 --- a/src/hotspot/share/oops/klass.cpp +++ b/src/hotspot/share/oops/klass.cpp @@ -828,9 +828,15 @@ void Klass::remove_java_mirror() { if (CDSConfig::is_dumping_heap()) { Klass* src_k = ArchiveBuilder::current()->get_source_addr(this); oop orig_mirror = src_k->java_mirror(); - oop scratch_mirror = HeapShared::scratch_java_mirror(orig_mirror); - if (scratch_mirror != nullptr) { - _archived_mirror_index = HeapShared::append_root(scratch_mirror); + if (orig_mirror == nullptr) { + assert(CDSConfig::is_dumping_final_static_archive(), "sanity"); + assert(is_instance_klass(), "sanity"); + assert(InstanceKlass::cast(this)->is_shared_unregistered_class(), "sanity"); + } else { + oop scratch_mirror = HeapShared::scratch_java_mirror(orig_mirror); + if (scratch_mirror != nullptr) { + _archived_mirror_index = HeapShared::append_root(scratch_mirror); + } } } #endif diff --git a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java index 9db886e20a5b6..03b5c415ceee1 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java @@ -31,11 +31,13 @@ * @requires vm.cds.supports.aot.class.linking * @comment work around JDK-8345635 * @requires !vm.jvmci.enabled - * @library /test/jdk/lib/testlibrary /test/lib + * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @build InitiatingLoaderTester BadOldClassA BadOldClassB - * @build BulkLoaderTest + * @build BulkLoaderTest SimpleCusty * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester * BadOldClassA BadOldClassB + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar + * SimpleCusty * @run driver BulkLoaderTest STATIC */ @@ -44,11 +46,13 @@ * @requires vm.cds.supports.aot.class.linking * @comment work around JDK-8345635 * @requires !vm.jvmci.enabled - * @library /test/jdk/lib/testlibrary /test/lib + * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @build InitiatingLoaderTester BadOldClassA BadOldClassB - * @build jdk.test.whitebox.WhiteBox BulkLoaderTest + * @build jdk.test.whitebox.WhiteBox BulkLoaderTest SimpleCusty * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester * BadOldClassA BadOldClassB + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar + * SimpleCusty * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox * @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. BulkLoaderTest DYNAMIC */ @@ -58,16 +62,20 @@ * @requires vm.cds.supports.aot.class.linking * @comment work around JDK-8345635 * @requires !vm.jvmci.enabled - * @library /test/jdk/lib/testlibrary /test/lib + * @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes * @build InitiatingLoaderTester BadOldClassA BadOldClassB - * @build BulkLoaderTest + * @build BulkLoaderTest SimpleCusty * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester * BadOldClassA BadOldClassB + * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar + * SimpleCusty * @run driver BulkLoaderTest AOT */ import java.io.File; import java.lang.StackWalker.StackFrame; +import java.net.URL; +import java.net.URLClassLoader; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -124,7 +132,7 @@ public String classpath(RunMode runMode) { @Override public String[] vmArgs(RunMode runMode) { return new String[] { - "-Xlog:cds,cds+aot+load", + "-Xlog:cds,cds+aot+load,cds+class=debug", "-XX:+AOTClassLinking", }; } @@ -140,6 +148,12 @@ public String[] appCommandLine(RunMode runMode) { public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception { if (isAOTWorkflow() && runMode == RunMode.TRAINING) { out.shouldContain("Skipping BadOldClassA: Unlinked class not supported by AOTConfiguration"); + out.shouldContain("Skipping SimpleCusty: Duplicated unregistered class"); + } + + if (isDumping(runMode)) { + // Check that we are archiving classes for custom class loaders. + out.shouldMatch("cds,class.* SimpleCusty"); } } } @@ -152,6 +166,7 @@ public static void main(String args[]) throws Exception { checkClasses(); checkInitiatingLoader(); checkOldClasses(); + checkCustomLoader(); } // Check the ClassLoader/Module/Package/ProtectionDomain/CodeSource of classes that are aot-linked @@ -275,6 +290,24 @@ static void checkOldClasses() throws Exception { System.out.println("Caught VerifyError for BadOldClassB: " + e); } } + + + static void checkCustomLoader() throws Exception { + for (int i = 0; i < 2; i++) { + Object o = initFromCustomLoader(); + System.out.println(o); + } + } + + static Object initFromCustomLoader() throws Exception { + String path = "cust.jar"; + URL url = new File(path).toURI().toURL(); + URL[] urls = new URL[] {url}; + URLClassLoader urlClassLoader = + new URLClassLoader("MyLoader", urls, null); + Class c = Class.forName("SimpleCusty", true, urlClassLoader); + return c.newInstance(); + } } class MyUtil { diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java index 205901e376998..4563aa60c89f5 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2023, 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 @@ -51,7 +51,7 @@ static void test() throws Exception { "-Xlog:class+load,cds=debug,cds+dynamic", "-cp", appJar, mainClass, appJar, "init", "keep-alive") .assertNormalExit(output -> { - output.shouldMatch("Skipping.LambHello[$][$]Lambda.*0x.*:.Hidden.class") + output.shouldMatch("Skipping.LambHello[$][$]Lambda.*0x.*:.Unreferenced.hidden.class") .shouldHaveExitValue(0); }); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaProxyCallerIsHidden.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaProxyCallerIsHidden.java index 0c8cad0b3a604..4ec7e81737891 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaProxyCallerIsHidden.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaProxyCallerIsHidden.java @@ -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 @@ -59,8 +59,8 @@ static void test() throws Exception { "-Xlog:class+load,cds+dynamic,cds=debug", "-cp", appJar, mainClass) .assertNormalExit(output -> { - output.shouldMatch("Skipping.LambdaHello_0x.*[$][$]Lambda.*:.Hidden.class") - .shouldMatch("Skipping.LambdaHello.0x.*:.Hidden.class") + output.shouldMatch("Skipping.LambdaHello_0x.*[$][$]Lambda.*:.Unreferenced.hidden.class") + .shouldMatch("Skipping.LambdaHello.0x.*:.Unreferenced.hidden.class") .shouldHaveExitValue(0); }); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java index 957e4cb647979..7793fd07bc366 100644 --- a/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java +++ b/test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/RegularHiddenClass.java @@ -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 @@ -57,7 +57,7 @@ static void test() throws Exception { "-Xlog:class+load=debug,cds+dynamic,cds=debug", "-cp", appJar, mainClass, "keep-alive") .assertNormalExit(output -> { - output.shouldMatch("cds.*Skipping.TestClass.0x.*Hidden.class") + output.shouldMatch("cds.*Skipping.TestClass.0x.*Unreferenced.hidden.class") .shouldNotMatch("cds.dynamic.*Archiving.hidden.TestClass.*") .shouldHaveExitValue(0); }); diff --git a/test/hotspot/jtreg/runtime/cds/appcds/test-classes/SimpleCusty.java b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/SimpleCusty.java new file mode 100644 index 0000000000000..9c82e0eb15d23 --- /dev/null +++ b/test/hotspot/jtreg/runtime/cds/appcds/test-classes/SimpleCusty.java @@ -0,0 +1,30 @@ +/* + * 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. + * + */ + +// This class is to be loaded by a custom class loader. +public class SimpleCusty { + public String toString() { + return "Instance of SimpleCusty"; + } +}