<!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>