diff --git a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java index b0ee27f36a702..aa4d48b720e97 100644 --- a/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java +++ b/src/jdk.internal.vm.ci/share/classes/jdk/vm/ci/hotspot/HotSpotJVMCIRuntime.java @@ -35,6 +35,7 @@ import java.lang.invoke.CallSite; import java.lang.invoke.ConstantCallSite; import java.lang.invoke.MethodHandle; +import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.lang.reflect.Executable; import java.lang.reflect.Field; @@ -499,7 +500,32 @@ T get() { } } - @NativeImageReinitialize private HashMap> resolvedJavaTypes; + + /** + * A weak reference that also tracks the key used to insert the value into {@link #resolvedJavaTypes} so that + * it can be removed when the referent is cleared. + */ + static class KlassWeakReference extends WeakReference { + + private final Long klassPointer; + + public KlassWeakReference(Long klassPointer, HotSpotResolvedObjectTypeImpl referent, ReferenceQueue q) { + super(referent, q); + this.klassPointer = klassPointer; + } + } + + /** + * A mapping from the {@code Klass*} to the corresponding {@link HotSpotResolvedObjectTypeImpl}. The value is + * held weakly through a {@link KlassWeakReference} so that unused types can be unloaded when the compiler no longer needs them. + */ + @NativeImageReinitialize private HashMap resolvedJavaTypes; + + /** + * A {@link ReferenceQueue} to track when {@link KlassWeakReference}s have been freed so that the corresponding + * entry in {@link #resolvedJavaTypes} can be cleared. + */ + @NativeImageReinitialize private ReferenceQueue resolvedJavaTypesQueue; /** * Stores the value set by {@link #excludeFromJVMCICompilation(Module...)} so that it can be @@ -662,24 +688,41 @@ HotSpotResolvedJavaType fromClass(Class javaClass) { return fromClass0(javaClass); } - synchronized HotSpotResolvedObjectTypeImpl fromMetaspace(long klassPointer) { + synchronized HotSpotResolvedObjectTypeImpl fromMetaspace(Long klassPointer) { if (resolvedJavaTypes == null) { resolvedJavaTypes = new HashMap<>(); + resolvedJavaTypesQueue = new ReferenceQueue<>(); } assert klassPointer != 0; - WeakReference klassReference = resolvedJavaTypes.get(klassPointer); + KlassWeakReference klassReference = resolvedJavaTypes.get(klassPointer); HotSpotResolvedObjectTypeImpl javaType = null; if (klassReference != null) { - javaType = (HotSpotResolvedObjectTypeImpl) klassReference.get(); + javaType = klassReference.get(); } if (javaType == null) { String name = compilerToVm.getSignatureName(klassPointer); javaType = new HotSpotResolvedObjectTypeImpl(klassPointer, name); - resolvedJavaTypes.put(klassPointer, new WeakReference<>(javaType)); + resolvedJavaTypes.put(klassPointer, new KlassWeakReference(klassPointer, javaType, resolvedJavaTypesQueue)); } + expungeStaleKlassEntries(); return javaType; } + + /** + * Clean up WeakReferences whose referents have been cleared. This should be called from a synchronized context. + */ + private void expungeStaleKlassEntries() { + KlassWeakReference current = (KlassWeakReference) resolvedJavaTypesQueue.poll(); + while (current != null) { + // Make sure the entry is still mapped to the weak reference + if (resolvedJavaTypes.get(current.klassPointer) == current) { + resolvedJavaTypes.remove(current.klassPointer); + } + current = (KlassWeakReference) resolvedJavaTypesQueue.poll(); + } + } + private JVMCIBackend registerBackend(JVMCIBackend backend) { Class arch = backend.getCodeCache().getTarget().arch.getClass(); JVMCIBackend oldValue = backends.put(arch, backend);