JDK-8311071 : Avoid SoftReferences in LambdaFormEditor and MethodTypeForm when storing heap objects into AOT cache
  • Type: Enhancement
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2023-06-28
  • Updated: 2024-11-15
  • Resolved: 2024-11-15
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 24
24Resolved
Related Reports
Blocks :  
Duplicate :  
Relates :  
Description
The implementation of java.lang.invoke uses SoftReferences so that unused MethodHandles, LambdaForms, etc, can be garbage collected.

However, if we want to store java.lang.invoke objects in the CDS archive (e.g., JDK-8293336), it's difficult to cache these SoftReferences. SoftReferences in turn point to ReferenceQueues, etc, which have dependencies on the current execution state (Threads, etc) which are difficult to cache.

The proposal is to add a new flag: MethodHandleStatics.NO_SOFT_CACHE. When this flag is true, we avoid using SoftReferences, and store a direct reference to the target object instead.

JDK-8293336 stores only java.lang.invoke objects that refer to classes loaded by the boot/platform/app loaders. These classes are never unloaded, so it's not necessary to point to them using SoftReferences.

This RFE modifies only the LambdaFormEditor and MethodTypeForm classes, as that's the minimal modification required by JDK-8293336.

(See comments below to alternatives)

Example: src/java.base/share/classes/java/lang/invoke/MethodTypeForm.java

Before:
    final SoftReference<MethodHandle>[] methodHandles;

    public MethodHandle cachedMethodHandle(int which) {
        SoftReference<MethodHandle> entry = methodHandles[which];
        return (entry != null) ? entry.get() : null;
    }

    public synchronized MethodHandle setCachedMethodHandle(int which, MethodHandle mh) {
        // Simulate a CAS, to avoid racy duplication of results.
        SoftReference<MethodHandle> entry = methodHandles[which];
        if (entry != null) {
            MethodHandle prev = entry.get();
            if (prev != null) {
                return prev;
            }
        }
        methodHandles[which] = new SoftReference<>(mh);
        return mh;
    }

After:

    private final Object[] methodHandles;

   @SuppressWarnings({"rawtypes", "unchecked"})
    public MethodHandle cachedMethodHandle(int which) {
        Object entry = methodHandles[which];
        if (entry == null) {
            return null;
        } else if (entry instanceof MethodHandle) {
            return (MethodHandle) entry;
        } else {
            return ((SoftReference<MethodHandle>)entry).get();
        }
    }

    public synchronized MethodHandle setCachedMethodHandle(int which, MethodHandle mh) {
        // Simulate a CAS, to avoid racy duplication of results.
        MethodHandle prev = cachedMethodHandle(which);
        if (prev != null) {
            return prev;
        }
        if (NO_SOFT_CACHE) {
            methodHandles[which] = mh;
        } else {
            methodHandles[which] = new SoftReference<>(mh);
        }
        return mh;
    }

==============
NOTE: this is a short term solution so we can start archiving java.lang.invoke objects. At this point, we are willing to accept a few unwanted objects that might have otherwise been garbage collected during CDS dump time.

A more general solution is proposed in JDK-8311078
Comments
Fixed as part of JDK-8331497: Implement JEP 483: Ahead-of-Time Class Loading & Linking
15-11-2024

A pull request was submitted for review. Branch: pr/20959 URL: https://git.openjdk.org/jdk/pull/21049 Date: 2024-09-17 23:48:11 +0000
17-09-2024

[~forax] when methodHandles[] is stored in the CDS archive, it is of type Object[]. However, it may have some empty slots. During the production run, we might store SoftReference into the empty slots. Therefore, we must check the type of each element.
17-09-2024

Has been implemented in Leyden. Need to upstream to mainline.
20-06-2024

In cachedMethodHandle, checking the type of the array should be more efficient than checking the type of the array cell. if (methodHandles.getClass() == MethodHandle[].class) { ...
28-06-2023

An alternative would be for CDS to scrape off the unarchivable portions of the SoftReferences. At run time, when the archived objects are loaded, we revive each SoftReference by binding them to the appropriate ReferenceQueue, etc. However, it will be difficult to support SoftReference that were bound to a non-standard ReferenceQueue.
28-06-2023