<!DOCTYPE html><html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<p><br>
</p>
<div class="moz-cite-prefix">On 1/29/26 10:48 AM, David Lloyd wrote:<br>
</div>
<blockquote type="cite" cite="mid:CANghgrQesBAOOwUjLrRE9dcwOW=PdBS5wTKekkinhpiJpiZofQ@mail.gmail.com">
<div dir="ltr">
<div dir="ltr">
<div class="gmail_default" style="font-family:arial,helvetica,sans-serif">This is very
exciting for Quarkus in particular! However one concern I
have is that custom class loaders are often not using the
parent-delegation model. Our custom class loaders may, in
some cases, link directly to other class loaders based on a
graph of interdependencies rather than the classic
delegation tree.</div>
<div class="gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
</div>
</div>
</blockquote>
<p>My current proposal can't easily support circular dependencies
between loaders. For example,</p>
<p>1. Subtype dependencies: A is a subtype of B, which is a subtype
of C. A and C are defined in loader 1 and B is defined in loader
2.</p>
<p>2. Constant pool dependencies: A and B are defined in two
different loaders</p>
<p> class A {<br>
static void foo(B b) { b.bar(); }<br>
}</p>
<p> class B extends A {<br>
static void b.bar();<br>
}</p>
<p>Perhaps we need a way to define a group of inter-related loaders
like</p>
<p>AOTLoaderUtils.setAOTCompatibe(<br>
UID1, loader1,<br>
UID2, loader2,<br>
UID3, loader3,<br>
UID4, loader4)<br>
<br>
</p>
<p>So all these loaders are recorded and restored as a group?</p>
<br>
<blockquote type="cite" cite="mid:CANghgrQesBAOOwUjLrRE9dcwOW=PdBS5wTKekkinhpiJpiZofQ@mail.gmail.com">
<div dir="ltr">
<div dir="ltr">
<div class="gmail_default" style="font-family:arial,helvetica,sans-serif">In this
situation, I would be concerned about constant pool
pre-linking. Could the linked constants perhaps somehow
"remember" what class loader they were previously linked
against? One would not expect this to change in a
well-behaved class loader arrangement.</div>
<div class="gmail_default" style="font-family:arial,helvetica,sans-serif"><br>
</div>
</div>
</div>
</blockquote>
<p>Each set of classes (defined by the same loader) remember all the
other loaders that they have references to. This set will not be
restored from the AOT cache unless all the other loaders are also
restored from the AOT cache.</p>
<p>If the dependencies forms a tree, then you can restore the
loaders individually, but in the correct order.</p>
<p>If there are circular dependencies, then you'd need to restore
them together as a group.</p>
<p>Thanks</p>
<p>- Ioi</p>
<p><br>
</p>
<p><br>
</p>
<blockquote type="cite" cite="mid:CANghgrQesBAOOwUjLrRE9dcwOW=PdBS5wTKekkinhpiJpiZofQ@mail.gmail.com">
<div dir="ltr">
<div dir="ltr">
<div class="gmail_default" style="font-family:arial,helvetica,sans-serif">Thanks!</div>
</div>
<br>
<div class="gmail_quote gmail_quote_container">
<div dir="ltr" class="gmail_attr">On Wed, Jan 28, 2026 at
4:01 PM <<a href="mailto:ioi.lam@oracle.com" moz-do-not-send="true" class="moz-txt-link-freetext">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://urldefense.com/v3/__https://github.com/openjdk/jdk/pull/29472__;!!ACWV5N9M2RV99hQ!PWfJNuimWLiP5BnpyeWBxecYOMqoai-z0bnS7KRbYvXpfL-4lafPU6vAv_zR5RvCyQzly57FytzQaN9oRuY$" rel="noreferrer" target="_blank" moz-do-not-send="true">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>
<div><br clear="all">
</div>
<div><br>
</div>
<span class="gmail_signature_prefix">-- </span><br>
<div dir="ltr" class="gmail_signature">
<div dir="ltr">- DML • he/him<br>
</div>
</div>
</div>
</blockquote>
</body>
</html>