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