Skip to content

Commit e3f26b0

Browse files
committed
8351319: AOT cache support for custom class loaders broken since JDK-8348426
Reviewed-by: ccheung, matsaave, jrose
1 parent 4954a33 commit e3f26b0

File tree

13 files changed

+185
-34
lines changed

13 files changed

+185
-34
lines changed

src/hotspot/share/cds/aotArtifactFinder.cpp

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,11 @@ void AOTArtifactFinder::find_artifacts() {
152152
SystemDictionaryShared::dumptime_table()->iterate_all_live_classes([&] (InstanceKlass* k, DumpTimeClassInfo& info) {
153153
if (!info.is_excluded() && _seen_classes->get(k) == nullptr) {
154154
info.set_excluded();
155-
if (log_is_enabled(Info, cds)) {
155+
info.set_has_checked_exclusion();
156+
if (log_is_enabled(Debug, cds)) {
156157
ResourceMark rm;
157-
log_info(cds)("Skipping %s: %s class", k->name()->as_C_string(),
158-
k->is_hidden() ? "Hidden" : "AOT tooling");
158+
log_debug(cds)("Skipping %s: %s class", k->name()->as_C_string(),
159+
k->is_hidden() ? "Unreferenced hidden" : "AOT tooling");
159160
}
160161
}
161162
});
@@ -211,6 +212,10 @@ void AOTArtifactFinder::add_cached_instance_class(InstanceKlass* ik) {
211212
_seen_classes->put_if_absent(ik, &created);
212213
if (created) {
213214
_all_cached_classes->append(ik);
215+
if (CDSConfig::is_dumping_final_static_archive() && ik->is_shared_unregistered_class()) {
216+
// The following are not appliable to unregistered classes
217+
return;
218+
}
214219
scan_oops_in_instance_class(ik);
215220
if (ik->is_hidden() && CDSConfig::is_initing_classes_at_dump_time()) {
216221
bool succeed = AOTClassLinker::try_add_candidate(ik);

src/hotspot/share/cds/dumpTimeClassInfo.inline.hpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
/*
3-
* Copyright (c) 2021, 2023, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2021, 2025, Oracle and/or its affiliates. All rights reserved.
44
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
55
*
66
* This code is free software; you can redistribute it and/or modify it
@@ -28,6 +28,7 @@
2828

2929
#include "cds/dumpTimeClassInfo.hpp"
3030

31+
#include "cds/cdsConfig.hpp"
3132
#include "classfile/systemDictionaryShared.hpp"
3233
#include "classfile/classLoaderData.inline.hpp"
3334
#include "oops/instanceKlass.hpp"
@@ -44,7 +45,10 @@ void DumpTimeSharedClassTable::iterate_all_live_classes(Function function) const
4445
auto wrapper = [&] (InstanceKlass* k, DumpTimeClassInfo& info) {
4546
assert(SafepointSynchronize::is_at_safepoint(), "invariant");
4647
assert_lock_strong(DumpTimeTable_lock);
47-
if (k->is_loader_alive()) {
48+
if (CDSConfig::is_dumping_final_static_archive() && !k->is_loaded()) {
49+
assert(k->is_shared_unregistered_class(), "must be");
50+
function(k, info);
51+
} else if (k->is_loader_alive()) {
4852
function(k, info);
4953
assert(k->is_loader_alive(), "must not change");
5054
} else {

src/hotspot/share/cds/finalImageRecipes.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,11 @@ void FinalImageRecipes::load_all_classes(TRAPS) {
102102
Klass* k = _all_klasses->at(i);
103103
if (k->is_instance_klass()) {
104104
InstanceKlass* ik = InstanceKlass::cast(k);
105-
if (!ik->is_shared_unregistered_class() && !ik->is_hidden()) {
105+
if (ik->is_shared_unregistered_class()) {
106+
SystemDictionaryShared::init_dumptime_info(ik);
107+
SystemDictionaryShared::add_unregistered_class(THREAD, ik);
108+
SystemDictionaryShared::copy_unregistered_class_size_and_crc32(ik);
109+
} else if (!ik->is_hidden()) {
106110
Klass* actual = SystemDictionary::resolve_or_fail(ik->name(), class_loader, true, CHECK);
107111
if (actual != ik) {
108112
ResourceMark rm(THREAD);

src/hotspot/share/classfile/systemDictionaryShared.cpp

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -465,6 +465,21 @@ bool SystemDictionaryShared::add_unregistered_class(Thread* current, InstanceKla
465465
return (klass == *v);
466466
}
467467

468+
void SystemDictionaryShared::copy_unregistered_class_size_and_crc32(InstanceKlass* klass) {
469+
precond(CDSConfig::is_dumping_final_static_archive());
470+
precond(klass->is_shared());
471+
472+
// A shared class must have a RunTimeClassInfo record
473+
const RunTimeClassInfo* record = find_record(&_static_archive._unregistered_dictionary,
474+
nullptr, klass->name());
475+
precond(record != nullptr);
476+
precond(record->klass() == klass);
477+
478+
DumpTimeClassInfo* info = get_info(klass);
479+
info->_clsfile_size = record->crc()->_clsfile_size;
480+
info->_clsfile_crc32 = record->crc()->_clsfile_crc32;
481+
}
482+
468483
void SystemDictionaryShared::set_shared_class_misc_info(InstanceKlass* k, ClassFileStream* cfs) {
469484
assert(CDSConfig::is_dumping_archive(), "sanity");
470485
assert(!is_builtin(k), "must be unregistered class");
@@ -667,7 +682,7 @@ bool SystemDictionaryShared::should_be_excluded(Klass* k) {
667682
}
668683

669684
void SystemDictionaryShared::finish_exclusion_checks() {
670-
if (CDSConfig::is_dumping_dynamic_archive()) {
685+
if (CDSConfig::is_dumping_dynamic_archive() || CDSConfig::is_dumping_preimage_static_archive()) {
671686
// Do this first -- if a base class is excluded due to duplication,
672687
// all of its subclasses will also be excluded.
673688
ResourceMark rm;

src/hotspot/share/classfile/systemDictionaryShared.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ class SystemDictionaryShared: public SystemDictionary {
248248
return (k->shared_classpath_index() != UNREGISTERED_INDEX);
249249
}
250250
static bool add_unregistered_class(Thread* current, InstanceKlass* k);
251+
static void copy_unregistered_class_size_and_crc32(InstanceKlass* klass);
251252

252253
static void finish_exclusion_checks();
253254
static DumpTimeSharedClassTable* dumptime_table() { return _dumptime_table; }

src/hotspot/share/oops/constantPool.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,9 +476,23 @@ void ConstantPool::remove_unshareable_info() {
476476
return;
477477
}
478478

479+
bool update_resolved_reference = true;
480+
if (CDSConfig::is_dumping_final_static_archive()) {
481+
ConstantPool* src_cp = ArchiveBuilder::current()->get_source_addr(this);
482+
InstanceKlass* src_holder = src_cp->pool_holder();
483+
if (src_holder->is_shared_unregistered_class()) {
484+
// Unregistered classes are not loaded in the AOT assembly phase. The resolved reference length
485+
// is already saved during the training run.
486+
precond(!src_holder->is_loaded());
487+
precond(resolved_reference_length() >= 0);
488+
precond(resolved_references() == nullptr);
489+
update_resolved_reference = false;
490+
}
491+
}
492+
479493
// resolved_references(): remember its length. If it cannot be restored
480494
// from the archived heap objects at run time, we need to dynamically allocate it.
481-
if (cache() != nullptr) {
495+
if (update_resolved_reference && cache() != nullptr) {
482496
set_resolved_reference_length(
483497
resolved_references() != nullptr ? resolved_references()->length() : 0);
484498
set_resolved_references(OopHandle());

src/hotspot/share/oops/klass.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -828,9 +828,15 @@ void Klass::remove_java_mirror() {
828828
if (CDSConfig::is_dumping_heap()) {
829829
Klass* src_k = ArchiveBuilder::current()->get_source_addr(this);
830830
oop orig_mirror = src_k->java_mirror();
831-
oop scratch_mirror = HeapShared::scratch_java_mirror(orig_mirror);
832-
if (scratch_mirror != nullptr) {
833-
_archived_mirror_index = HeapShared::append_root(scratch_mirror);
831+
if (orig_mirror == nullptr) {
832+
assert(CDSConfig::is_dumping_final_static_archive(), "sanity");
833+
assert(is_instance_klass(), "sanity");
834+
assert(InstanceKlass::cast(this)->is_shared_unregistered_class(), "sanity");
835+
} else {
836+
oop scratch_mirror = HeapShared::scratch_java_mirror(orig_mirror);
837+
if (scratch_mirror != nullptr) {
838+
_archived_mirror_index = HeapShared::append_root(scratch_mirror);
839+
}
834840
}
835841
}
836842
#endif

test/hotspot/jtreg/runtime/cds/appcds/aotClassLinking/BulkLoaderTest.java

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,14 @@
3131
* @requires vm.cds.supports.aot.class.linking
3232
* @comment work around JDK-8345635
3333
* @requires !vm.jvmci.enabled
34-
* @library /test/jdk/lib/testlibrary /test/lib
34+
* @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
3535
* @build InitiatingLoaderTester BadOldClassA BadOldClassB
36-
* @build BulkLoaderTest
36+
* @build jdk.test.whitebox.WhiteBox BulkLoaderTest SimpleCusty
3737
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester
3838
* BadOldClassA BadOldClassB
39+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar
40+
* SimpleCusty
41+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox
3942
* @run driver BulkLoaderTest STATIC
4043
*/
4144

@@ -44,30 +47,37 @@
4447
* @requires vm.cds.supports.aot.class.linking
4548
* @comment work around JDK-8345635
4649
* @requires !vm.jvmci.enabled
47-
* @library /test/jdk/lib/testlibrary /test/lib
50+
* @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
4851
* @build InitiatingLoaderTester BadOldClassA BadOldClassB
49-
* @build jdk.test.whitebox.WhiteBox BulkLoaderTest
52+
* @build jdk.test.whitebox.WhiteBox BulkLoaderTest SimpleCusty
5053
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester
5154
* BadOldClassA BadOldClassB
52-
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
53-
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. BulkLoaderTest DYNAMIC
55+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar
56+
* SimpleCusty
57+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox
58+
* @run main/othervm -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:WhiteBox.jar BulkLoaderTest DYNAMIC
5459
*/
5560

5661
/*
5762
* @test id=aot
5863
* @requires vm.cds.supports.aot.class.linking
5964
* @comment work around JDK-8345635
6065
* @requires !vm.jvmci.enabled
61-
* @library /test/jdk/lib/testlibrary /test/lib
62-
* @build InitiatingLoaderTester BadOldClassA BadOldClassB
63-
* @build BulkLoaderTest
66+
* @library /test/jdk/lib/testlibrary /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes
67+
* @build jdk.test.whitebox.WhiteBox InitiatingLoaderTester BadOldClassA BadOldClassB
68+
* @build BulkLoaderTest SimpleCusty
6469
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar BulkLoaderTestApp.jar BulkLoaderTestApp MyUtil InitiatingLoaderTester
6570
* BadOldClassA BadOldClassB
71+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar WhiteBox.jar jdk.test.whitebox.WhiteBox
72+
* @run driver jdk.test.lib.helpers.ClassFileInstaller -jar cust.jar
73+
* SimpleCusty
6674
* @run driver BulkLoaderTest AOT
6775
*/
6876

6977
import java.io.File;
7078
import java.lang.StackWalker.StackFrame;
79+
import java.net.URL;
80+
import java.net.URLClassLoader;
7181
import java.util.List;
7282
import java.util.regex.Matcher;
7383
import java.util.regex.Pattern;
@@ -76,6 +86,7 @@
7686
import jdk.test.lib.cds.CDSAppTester;
7787
import jdk.test.lib.helpers.ClassFileInstaller;
7888
import jdk.test.lib.process.OutputAnalyzer;
89+
import jdk.test.whitebox.WhiteBox;
7990

8091
public class BulkLoaderTest {
8192
static final String appJar = ClassFileInstaller.getJarPath("BulkLoaderTestApp.jar");
@@ -114,6 +125,7 @@ public static void main(String[] args) throws Exception {
114125
static class Tester extends CDSAppTester {
115126
public Tester() {
116127
super(mainClass);
128+
useWhiteBox(ClassFileInstaller.getJarPath("WhiteBox.jar"));
117129
}
118130

119131
@Override
@@ -124,7 +136,7 @@ public String classpath(RunMode runMode) {
124136
@Override
125137
public String[] vmArgs(RunMode runMode) {
126138
return new String[] {
127-
"-Xlog:cds,cds+aot+load",
139+
"-Xlog:cds,cds+aot+load,cds+class=debug",
128140
"-XX:+AOTClassLinking",
129141
};
130142
}
@@ -140,6 +152,12 @@ public String[] appCommandLine(RunMode runMode) {
140152
public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {
141153
if (isAOTWorkflow() && runMode == RunMode.TRAINING) {
142154
out.shouldContain("Skipping BadOldClassA: Unlinked class not supported by AOTConfiguration");
155+
out.shouldContain("Skipping SimpleCusty: Duplicated unregistered class");
156+
}
157+
158+
if (isDumping(runMode)) {
159+
// Check that we are archiving classes for custom class loaders.
160+
out.shouldMatch("cds,class.* SimpleCusty");
143161
}
144162
}
145163
}
@@ -152,6 +170,7 @@ public static void main(String args[]) throws Exception {
152170
checkClasses();
153171
checkInitiatingLoader();
154172
checkOldClasses();
173+
checkCustomLoader();
155174
}
156175

157176
// Check the ClassLoader/Module/Package/ProtectionDomain/CodeSource of classes that are aot-linked
@@ -275,6 +294,38 @@ static void checkOldClasses() throws Exception {
275294
System.out.println("Caught VerifyError for BadOldClassB: " + e);
276295
}
277296
}
297+
298+
299+
static void checkCustomLoader() throws Exception {
300+
WhiteBox wb = WhiteBox.getWhiteBox();
301+
for (int i = 0; i < 2; i++) {
302+
Object o = initFromCustomLoader();
303+
System.out.println(o);
304+
Class c = o.getClass();
305+
if (wb.isSharedClass(BulkLoaderTestApp.class)) {
306+
// We are running with BulkLoaderTestApp from the AOT cache (or CDS achive)
307+
if (i == 0) {
308+
if (!wb.isSharedClass(c)) {
309+
throw new RuntimeException("The first loader should load SimpleCusty from AOT cache (or CDS achive)");
310+
}
311+
} else {
312+
if (wb.isSharedClass(c)) {
313+
throw new RuntimeException("The second loader should not load SimpleCusty from AOT cache (or CDS achive)");
314+
}
315+
}
316+
}
317+
}
318+
}
319+
320+
static Object initFromCustomLoader() throws Exception {
321+
String path = "cust.jar";
322+
URL url = new File(path).toURI().toURL();
323+
URL[] urls = new URL[] {url};
324+
URLClassLoader urlClassLoader =
325+
new URLClassLoader("MyLoader", urls, null);
326+
Class c = Class.forName("SimpleCusty", true, urlClassLoader);
327+
return c.newInstance();
328+
}
278329
}
279330

280331
class MyUtil {

test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaCustomLoader.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2023, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -51,7 +51,7 @@ static void test() throws Exception {
5151
"-Xlog:class+load,cds=debug,cds+dynamic",
5252
"-cp", appJar, mainClass, appJar, "init", "keep-alive")
5353
.assertNormalExit(output -> {
54-
output.shouldMatch("Skipping.LambHello[$][$]Lambda.*0x.*:.Hidden.class")
54+
output.shouldMatch("Skipping.LambHello[$][$]Lambda.*0x.*:.Unreferenced.hidden.class")
5555
.shouldHaveExitValue(0);
5656
});
5757

test/hotspot/jtreg/runtime/cds/appcds/dynamicArchive/LambdaProxyCallerIsHidden.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -59,8 +59,8 @@ static void test() throws Exception {
5959
"-Xlog:class+load,cds+dynamic,cds=debug",
6060
"-cp", appJar, mainClass)
6161
.assertNormalExit(output -> {
62-
output.shouldMatch("Skipping.LambdaHello_0x.*[$][$]Lambda.*:.Hidden.class")
63-
.shouldMatch("Skipping.LambdaHello.0x.*:.Hidden.class")
62+
output.shouldMatch("Skipping.LambdaHello_0x.*[$][$]Lambda.*:.Unreferenced.hidden.class")
63+
.shouldMatch("Skipping.LambdaHello.0x.*:.Unreferenced.hidden.class")
6464
.shouldHaveExitValue(0);
6565
});
6666

0 commit comments

Comments
 (0)