AOTHolder pattern for caching heap objects

ioi.lam at oracle.com ioi.lam at oracle.com
Thu Aug 29 17:10:32 UTC 2024


For the development of JDK-8293336 [1], I am experimenting with a 
pattern of Java code to indicate what heap objects should be stored in 
the AOT cache. This code has been pushed to the premain branch.

It looks like this [2]:

class java.lang.invoke.MethodType {

     static class AOTHolder {
         private static final @Stable MethodType[] objectOnlyTypes = new 
MethodType[20];
         private static @Stable HashMap<MethodType,MethodType> 
archivedMethodTypes;
     }

Essentially the class MethodType$AOTHolder will be stored in its 
"already initialized" state in the AOT cache. In the production run, 
MethodType$AOTHolder.<clinit> will NOT be run. Its static fields are 
already initialized and point to the objects that were created during 
the assembly phase.

Currently, the JVM is hard-wired to recognize only a few known AOTHolder 
classes. This pattern is used strictly for the AOT-caching of 
MethodType.internTable.

I want to hear people's reaction to this. I think other internal JDK 
classes can use a similar pattern as well.

(In the long term, we will need a more generalized way (a.k.a. the 
"Scrambled Egg API"), but I need to have a mechanism that works for the 
short term).

***

The reason I needed to use the AOTHolder pattern is to remove this 
horrible hack [3]. (It's now removed from the premain branch). You can 
see more info in the comments of [3], but the gist is:

- The old way of CDS heap object caching depends on re-executing the 
<clinit> methods of classes that want to cache heap objects

- These classes would call CDS.initializeFromArchive() to fetch the 
cached objects.

- Before CDS.initializeFromArchive() returns the objects, it wants to 
initialize all the classes of these objects (or else the caller would 
end up with an object whose class is not yet initialized)

- This scheme breaks down when several classes want to cache heap 
objects, and their <clinit> intertwine with each other.

The old code in [3] tries to work around a NullPointerException that 
happens due to the intertwining. That is certainly not a sustainable 
approach.

***

The AOTHolder pattern tries to accomplish two things

- Clearly identify what objects should be AOT-cached. In the case of 
MethodType.java, I moved only two of its many fields into its AOTHolder 
inner class.

- Cache the static fields in all the AOTHolder classes as a single 
snapshot. Avoid the need to run <clinit> so we don't need to worry about 
execution order, etc.

======

[1] https://bugs.openjdk.org/browse/JDK-8293336

[2] 
https://github.com/openjdk/leyden/blob/e33ecb3ec42d142569227b4bd1987124e5551cde/src/java.base/share/classes/java/lang/invoke/MethodType.java#L438-L441

[3] 
https://github.com/openjdk/leyden/blob/3a84df9d9860e743684e335282e3910b14cc982b/src/hotspot/share/cds/heapShared.cpp#L1415-L1466



More information about the leyden-dev mailing list