<div dir="ltr"><div>Hi Ioi,</div><div>Thanks for sharing your thoughts.</div><div>I have a hacky incomplete implementation for supporting custom loaders [0]. It is largely work-in-progress at the moment. It does share many of the ideas that you mentioned.</div><div>Instead of loader.setAOTCompatible(), it uses loader.setAOTIdentity(String).</div><div>I also updated the code to use CDS$UnregisteredClassLoader to load the classes during the assembly phase (based on the discussion in the leyden meeting last week).</div><div>But with this approach I hit an assert when the JVM tries to archive the java mirror of such classes. As these classes are loaded by CDS$UnregisteredClassLoader, their java mirror has a reference to Module object that in turn has reference to the instance of CDS$UnregisteredClassLoader, but this class is explicitly marked for exclusion. So we hit this assert in assembly phase:</div><div><br></div><div>#16 0x00007f2c89244fdd in report_vm_error (file=0x7f2c8a748510 "leyden/src/hotspot/share/cds/aotArtifactFinder.cpp", line=222, error_msg=0x7f2c8a748640 "assert(!SystemDictionaryShared::should_be_excluded(k)) failed", <br> detail_fmt=0x7f2c8a7485c0 "precond") at leyden/src/hotspot/share/utilities/debug.cpp:196<br>#17 0x00007f2c88d00b90 in AOTArtifactFinder::append_to_all_cached_classes (k=0x18002200) at leyden/src/hotspot/share/cds/aotArtifactFinder.cpp:222<br>#18 0x00007f2c88d00c2a in AOTArtifactFinder::add_cached_instance_class (ik=0x18002200) at leyden/src/hotspot/share/cds/aotArtifactFinder.cpp:236<br>#19 0x00007f2c88d00a15 in AOTArtifactFinder::add_aot_inited_class (ik=0x18002200) at leyden/src/hotspot/share/cds/aotArtifactFinder.cpp:197<br>#20 0x00007f2c8961643b in HeapShared::archive_object (obj=0x8088e1b8, referrer=0x8088e210, subgraph_info=0x7f2aec029308) at leyden/src/hotspot/share/cds/heapShared.cpp:675<br>#21 0x00007f2c8961ba50 in HeapShared::walk_one_object (stack=0x7f2c55bfa2c0, level=3, subgraph_info=0x7f2aec029308, orig_obj=0x8088e1b8, referrer=0x8088e210) at leyden/src/hotspot/share/cds/heapShared.cpp:2006<br>#22 0x00007f2c8961b28c in HeapShared::archive_reachable_objects_from (level=1, subgraph_info=0x7f2aec029308, orig_obj=0x80a64eb0) at leyden/src/hotspot/share/cds/heapShared.cpp:1914<br>#23 0x00007f2c89618012 in HeapShared::scan_java_mirror (orig_mirror=0x80890ee8) at leyden/src/hotspot/share/cds/heapShared.cpp:1066<br>#24 0x00007f2c89618135 in HeapShared::scan_java_class (orig_k=0x169dc710) at leyden/src/hotspot/share/cds/heapShared.cpp:107</div><div><br></div><div>To workaround I skip archiving the java mirror of these classes.</div><div><br></div><div>Other known issues with the current state of the prototype:</div><div>1. Restoring the protection domain in the production run. This is currently not implemented.</div><div>2. Call to AOTLinkedClassBulkLoader::link_classes_for_loader() hits an assert due to lock ordering issue</div><div><br></div><div>I have only tested it with an app with a single custom loader that loads a bunch of classes.</div><div>I am sure I will encounter more issues as I test it with more complex applications.</div><div>I will look into your patch to store java mirrors in the AOT config file and adopt it in my prototype. It should resolve the issues around archiving of java mirror and protection domain.</div><div><br></div><div>Any comments/feedback on the prototype is welcome.</div><div><br></div><div>[0] <a href="https://github.com/ashu-mehra/leyden/tree/custom-loader-support-v2">https://github.com/ashu-mehra/leyden/tree/custom-loader-support-v2</a></div><div><br></div><div>Thanks,</div><div><div dir="ltr" class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr">- Ashutosh Mehra</div></div></div><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Jan 28, 2026 at 5:01 PM <<a href="mailto:ioi.lam@oracle.com" target="_blank">ioi.lam@oracle.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">We have been brainstorming about supporting custom class loaders in the <br>
AOT cache. While the design is far from final, in our small group <br>
discussions, we seem to be converging on this:<br>
<br>
- Only custom class loaders that are known to produce *stable* results <br>
can store classes in the AOT cache<br>
- Stable result is roughly: when given a class name X, <br>
loader.loadClass(X) will always return a class with the same shape<br>
- Also, loader.loadClass(X) should not produce any observable side <br>
effects, other than the fact that class X has been loaded. E.g., don't <br>
set any static fields inside loadClass:<br>
- It's completely up to the custom class loader to decide whether it <br>
meets the AOT cache requirement.<br>
<br>
Some examples:<br>
- A URLClassLoader that loads from a fixed set of JAR files in the local <br>
file system that are known to never change<br>
- A code generator that always generates the same code shape given the <br>
same class name<br>
<br>
A counter example:<br>
- A code generator that mixes code with a random seed<br>
<br>
The handshake between the class loader and the AOT cache might look like <br>
this:<br>
<br>
URL[] urls = new URL[] {"foo.jar", "bar.jar"};<br>
URLClassLoader loader = new URLClassLoader(urls);<br>
String UID = "URLClassLoader$foo.jar:" + cksum("foo.jar") + <br>
"$bar.jar:" + cksum("bar.jar");<br>
loader.setAOTCompatible(UID);<br>
loader.loadClass("com.foo.Foo");<br>
loader.loadClass("com.bar.Bar");<br>
<br>
In the training run, the JVM will store all classes loaded by this <br>
loader into the AOT cache. These classes are tagged with the given UID.<br>
<br>
In the production run, when setAOTCompatible(UID) is called, the JVM <br>
checks if the AOT cache has any classes tagged with the UID. If so, <br>
these classes are automatically loaded into the loader *without any <br>
observable side effect*. Note that the usual handshake of <br>
ClassLoader::{findClass, loadClass, defineClass}, etc, does not happen. <br>
The classes simply appeared in the loader out of thin air.<br>
<br>
The UID provides a way for the loader to identify itself, as well as <br>
encoding the dependencies that were assumed during the training run. In <br>
the above example, we use the checksum of each JAR file to make sure <br>
that these files haven't changed (or disappeared).<br>
<br>
Note that we don't actually cache the loader object itself. The loader <br>
object will probably have references to environment states that cannot <br>
be safely stored into the AOT cache. Also, the creation of the loader <br>
during the training might produce side effects that cannot be easily <br>
captured into the AOT cache.<br>
<br>
We will likely have some restrictions on the behavior of the "AOT <br>
compatible" loaders<br>
<br>
- loader.setAOTCompatible() must be called before any class is defined <br>
in this loader. Otherwise setAOTCompatible() will throw an <br>
IllegalStateException<br>
- Only classes with simple ProtectionDomains will be stored into the AOT <br>
cache. For example, if the loader defines a class with a <br>
ProtectionDomain that uses a signed code source, the class will be <br>
excluded from the cache.<br>
<br>
Some implementation details:<br>
<br>
Ashutosh is working on a prototype. I think we can store the classes <br>
into the AOT configuration file at the end of the training run:<br>
<br>
- Store the Java mirror of the class into the AOT configuration file <br>
(this requires <a href="https://github.com/openjdk/jdk/pull/29472" rel="noreferrer" target="_blank">https://github.com/openjdk/jdk/pull/29472</a> )<br>
- Also save the ProtectionDomain in the mirror<br>
<br>
In the assembly phase, load the classes of each UID into an instance of <br>
jdk.internal.misc.CDS$UnregisteredClassLoader. This way we can handle <br>
classes of the same name defined in two different UIDs.<br>
If two UIDs have a parent/child relationship, we should recreate that <br>
with the UnregisteredClassLoader. This is needed for constant pool <br>
pre-linking..<br>
<br>
The above are just my random notes. Please add your thoughts.<br>
<br>
Thanks<br>
- Ioi<br>
<br>
</blockquote></div>