From 1905753b3768c1419c51abb60b9ed1b9ca364d81 Mon Sep 17 00:00:00 2001 From: Christian Wirth Date: Fri, 2 May 2025 10:20:58 +0200 Subject: [PATCH 1/3] clear cache in EnumSwitchPlugin after Analysis --- .../svm/hosted/phases/EnumSwitchPlugin.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/EnumSwitchPlugin.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/EnumSwitchPlugin.java index 3212d9565fc1..04b8b166ff3c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/EnumSwitchPlugin.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/phases/EnumSwitchPlugin.java @@ -95,8 +95,8 @@ public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod m, ValueNo * that end up in the same class or in the JDK. */ EnumSwitchFeature feature = ImageSingletons.lookup(EnumSwitchFeature.class); - method.ensureGraphParsed(feature.bb); - Boolean methodSafeForExecution = feature.methodsSafeForExecution.get(method); + method.ensureGraphParsed(feature.getBigBang()); + Boolean methodSafeForExecution = feature.isMethodsSafeForExecution(method); assert methodSafeForExecution != null : "after-parsing hook not executed for method " + method.format("%H.%n(%p)"); if (!methodSafeForExecution.booleanValue()) { return false; @@ -121,9 +121,9 @@ public boolean handleInvoke(GraphBuilderContext b, ResolvedJavaMethod m, ValueNo @AutomaticallyRegisteredFeature final class EnumSwitchFeature implements InternalFeature { - BigBang bb; + private BigBang bb; - final ConcurrentMap methodsSafeForExecution = new ConcurrentHashMap<>(); + private ConcurrentMap methodsSafeForExecution = new ConcurrentHashMap<>(); @Override public void duringSetup(DuringSetupAccess a) { @@ -142,10 +142,19 @@ private void onMethodParsed(AnalysisMethod method, StructuredGraph graph) { @Override public void afterAnalysis(AfterAnalysisAccess access) { bb = null; + methodsSafeForExecution = null; } @Override public void registerGraphBuilderPlugins(Providers providers, Plugins plugins, ParsingReason reason) { plugins.appendNodePlugin(new EnumSwitchPlugin(reason)); } + + Boolean isMethodsSafeForExecution(AnalysisMethod method) { + return methodsSafeForExecution.get(method); + } + + public BigBang getBigBang() { + return bb; + } } From 74bc0fc461fef88eddb06683bb4df097df29fb2c Mon Sep 17 00:00:00 2001 From: Christian Wirth Date: Thu, 1 May 2025 09:01:57 +0200 Subject: [PATCH 2/3] nullify maps after analysis in ReflectionDataBuilder --- .../oracle/svm/hosted/reflect/ReflectionDataBuilder.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java index 140b483d205d..ec098837c07a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/reflect/ReflectionDataBuilder.java @@ -145,8 +145,8 @@ public class ReflectionDataBuilder extends ConditionalConfigurationRegistry impl private final Map, Throwable> recordComponentsLookupExceptions = new ConcurrentHashMap<>(); // Intermediate bookkeeping - private final Map> processedTypes = new ConcurrentHashMap<>(); - private final Map, Set> pendingRecordClasses; + private Map> processedTypes = new ConcurrentHashMap<>(); + private Map, Set> pendingRecordClasses; record ConditionalTask(ConfigurationCondition condition, Consumer task) { } @@ -1133,9 +1133,9 @@ private static void reportLinkingErrors(Class clazz, List errors) protected void afterAnalysis() { sealed = true; - processedTypes.clear(); + processedTypes = null; if (!throwMissingRegistrationErrors()) { - pendingRecordClasses.clear(); + pendingRecordClasses = null; } } From c6ff33e72e725207a7ef7ddd01759f75f0a5bf24 Mon Sep 17 00:00:00 2001 From: Christian Wirth Date: Tue, 6 May 2025 09:30:41 +0200 Subject: [PATCH 3/3] clear caches in RuntimeMetadataEncoderImpl --- .../code/RuntimeMetadataEncoderImpl.java | 104 ++++++++++++++---- 1 file changed, 84 insertions(+), 20 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/RuntimeMetadataEncoderImpl.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/RuntimeMetadataEncoderImpl.java index e0e2d84601ea..605a619b532a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/RuntimeMetadataEncoderImpl.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/code/RuntimeMetadataEncoderImpl.java @@ -159,25 +159,25 @@ public RuntimeMetadataEncoder create(SnippetReflectionProvider snippetReflection private final CodeInfoEncoder.Encoders encoders; private final ReflectionDataAccessors accessors; private final ReflectionDataBuilder dataBuilder; - private final TreeSet sortedTypes = new TreeSet<>(Comparator.comparingLong(t -> t.getHub().getTypeID())); - private final Map classData = new HashMap<>(); - private final Map> fieldData = new HashMap<>(); - private final Map> methodData = new HashMap<>(); - private final Map> constructorData = new HashMap<>(); - - private final Map classLookupErrors = new HashMap<>(); - private final Map fieldLookupErrors = new HashMap<>(); - private final Map methodLookupErrors = new HashMap<>(); - private final Map constructorLookupErrors = new HashMap<>(); - private final Map recordComponentLookupErrors = new HashMap<>(); - - private final Set heapData = new HashSet<>(); - - private final Map annotationsEncodings = new HashMap<>(); - private final Map parameterAnnotationsEncodings = new HashMap<>(); - private final Map annotationDefaultEncodings = new HashMap<>(); - private final Map typeAnnotationsEncodings = new HashMap<>(); - private final Map reflectParametersEncodings = new HashMap<>(); + private TreeSet sortedTypes = new TreeSet<>(Comparator.comparingLong(t -> t.getHub().getTypeID())); + private Map classData = new HashMap<>(); + private Map> fieldData = new HashMap<>(); + private Map> methodData = new HashMap<>(); + private Map> constructorData = new HashMap<>(); + + private Map classLookupErrors = new HashMap<>(); + private Map fieldLookupErrors = new HashMap<>(); + private Map methodLookupErrors = new HashMap<>(); + private Map constructorLookupErrors = new HashMap<>(); + private Map recordComponentLookupErrors = new HashMap<>(); + + private Set heapData = new HashSet<>(); + + private Map annotationsEncodings = new HashMap<>(); + private Map parameterAnnotationsEncodings = new HashMap<>(); + private Map annotationDefaultEncodings = new HashMap<>(); + private Map typeAnnotationsEncodings = new HashMap<>(); + private Map reflectParametersEncodings = new HashMap<>(); public RuntimeMetadataEncoderImpl(SnippetReflectionProvider snippetReflection, CodeInfoEncoder.Encoders encoders) { this.snippetReflection = snippetReflection; @@ -853,7 +853,35 @@ public void encodeAllAndInstall() { } install(buf); /* Enable field recomputers in reflection objects to see the computed values */ - ImageSingletons.add(EncodedRuntimeMetadataSupplier.class, this); + EncodedRuntimeMetadataSupplierImpl supplierImpl = new EncodedRuntimeMetadataSupplierImpl(annotationsEncodings, parameterAnnotationsEncodings, annotationDefaultEncodings, + typeAnnotationsEncodings, reflectParametersEncodings); + clearDataAfterEncoding(); + ImageSingletons.add(EncodedRuntimeMetadataSupplier.class, supplierImpl); + } + + /** + * After the buffer has been created and installed, all other data can be cleaned. + */ + private void clearDataAfterEncoding() { + this.annotationsEncodings = null; + this.parameterAnnotationsEncodings = null; + this.annotationDefaultEncodings = null; + this.typeAnnotationsEncodings = null; + this.reflectParametersEncodings = null; + + this.sortedTypes = null; + this.classData = null; + this.fieldData = null; + this.methodData = null; + this.constructorData = null; + + this.classLookupErrors = null; + this.fieldLookupErrors = null; + this.methodLookupErrors = null; + this.constructorLookupErrors = null; + this.recordComponentLookupErrors = null; + + this.heapData = null; } private int encodeErrorIndex(Throwable error) { @@ -1271,4 +1299,40 @@ public static Object createFromLoader(ImageSingletonLoader loader) { return new LayeredRuntimeMetadataSingleton(registeredMethods, registeredFields); } } + + /** + * Container for data required in later phases. Cleaner separation, rest of + * RuntimeMetadataEncoderImpl can be cleaned after encoding. + */ + public record EncodedRuntimeMetadataSupplierImpl(Map annotationsEncodings, + Map parameterAnnotationsEncodings, + Map annotationDefaultEncodings, + Map typeAnnotationsEncodings, + Map reflectParametersEncodings) implements EncodedRuntimeMetadataSupplier { + + @Override + public byte[] getAnnotationsEncoding(AccessibleObject object) { + return annotationsEncodings.get(object); + } + + @Override + public byte[] getParameterAnnotationsEncoding(Executable object) { + return parameterAnnotationsEncodings.get(object); + } + + @Override + public byte[] getAnnotationDefaultEncoding(Method object) { + return annotationDefaultEncodings.get(object); + } + + @Override + public byte[] getTypeAnnotationsEncoding(AccessibleObject object) { + return typeAnnotationsEncodings.get(object); + } + + @Override + public byte[] getReflectParametersEncoding(Executable object) { + return reflectParametersEncodings.get(object); + } + } }