From 4880346c9d3491b7b367fcd1ca6bfc5c3fc3fa7f Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Tue, 11 Jul 2023 23:28:44 -0700 Subject: [PATCH 1/2] Allow all classes to be used at image build time --- substratevm/CHANGELOG.md | 1 + substratevm/mx.substratevm/mx_substratevm.py | 4 ++-- .../classinitialization/ClassInitializationOptions.java | 5 +++-- .../classinitialization/ClassInitializationSupport.java | 8 ++++---- .../hosted/image/DisallowedImageHeapObjectFeature.java | 9 ++++++++- .../oracle/svm/test/clinit/TestClassInitialization.java | 8 ++++---- 6 files changed, 22 insertions(+), 13 deletions(-) diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index b193a081f039..54f77d12343d 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -7,6 +7,7 @@ This changelog summarizes major changes to GraalVM Native Image. * (GR-45841) BellSoft added support for the JFR event ThreadCPULoad. * (GR-45994) Removed the option `-H:EnableSignalAPI`. Please use the runtime option `EnableSignalHandling` if it is necessary to enable or disable signal handling explicitly. * (GR-39406) Simulation of class initializer: Class initializer of classes that are not marked for initialization at image build time are simulated at image build time to avoid executing them at image run time. +* (GR-39406) All classes can now be used at image build time, even when they are not explicitly configured as `--initialize-at-build-time`. Note, however, that still only classes configured as `--initialize-at-build-time` are allowed in the image heap. * (GR-46392) Add `--parallelism` option to control how many threads are used by the build process. * (GR-46392) Add build resources section to the build output that shows the memory and thread limits of the build process. diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 710ed2a0a74c..e1e71aa6dfec 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -1413,9 +1413,9 @@ def build_and_test_clinittest_image(native_image, args, new_class_init_policy): mkpath(build_dir) if new_class_init_policy: - policy_args = ['-H:+UseNewExperimentalClassInitialization', '-H:+SimulateClassInitializer', '-H:Features=com.oracle.svm.test.clinit.TestClassInitializationFeatureNewPolicyFeature'] + policy_args = ['-H:-UseDeprecatedOldClassInitialization', '-H:+SimulateClassInitializer', '-H:Features=com.oracle.svm.test.clinit.TestClassInitializationFeatureNewPolicyFeature'] else: - policy_args = ['-H:-UseNewExperimentalClassInitialization', '-H:-SimulateClassInitializer', '-H:Features=com.oracle.svm.test.clinit.TestClassInitializationFeatureOldPolicyFeature'] + policy_args = ['-H:+UseDeprecatedOldClassInitialization', '-H:-SimulateClassInitializer', '-H:Features=com.oracle.svm.test.clinit.TestClassInitializationFeatureOldPolicyFeature'] # Build and run the example native_image( diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationOptions.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationOptions.java index b6128313be24..54fca49082cd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationOptions.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationOptions.java @@ -100,8 +100,9 @@ private static class InitializationValueEager extends InitializationValueTransfo @Option(help = "Assert class initialization is specified for all classes.", type = OptionType.Debug)// public static final HostedOptionKey AssertInitializationSpecifiedForAllClasses = new HostedOptionKey<>(false); - @Option(help = "Use new class initialization strategy that allows all classes to be used at image build time.", type = OptionType.Expert)// - public static final HostedOptionKey UseNewExperimentalClassInitialization = new HostedOptionKey<>(false); + @Option(help = "Use the old class initialization strategy that does not allow all classes to be used at image build time.", type = OptionType.Expert, // + deprecated = true, deprecationMessage = "Temporary flag to restore the class initialization behavior of older GraalVM versions. The old class initialization strategy will be removed in a future version of GraalVM.") // + public static final HostedOptionKey UseDeprecatedOldClassInitialization = new HostedOptionKey<>(false); @Option(help = "Simulate the effects of class initializer at image build time, to avoid class initialization at run time.", type = OptionType.Expert)// public static final HostedOptionKey SimulateClassInitializer = new HostedOptionKey<>(true); diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java index 3405e25009f6..d7c88e8e71e0 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationSupport.java @@ -87,11 +87,11 @@ public abstract class ClassInitializationSupport implements RuntimeClassInitiali final MetaAccessProvider metaAccess; public static ClassInitializationSupport create(MetaAccessProvider metaAccess, ImageClassLoader loader) { - if (ClassInitializationOptions.UseNewExperimentalClassInitialization.getValue()) { - LogUtils.warning("Using new experimental class initialization strategy. Image size and peak performance are not optimized yet!"); - return new AllowAllHostedUsagesClassInitializationSupport(metaAccess, loader); + if (ClassInitializationOptions.UseDeprecatedOldClassInitialization.getValue()) { + LogUtils.warning("Using old deprecated class initialization strategy. Only classes that are marked explicitly as '--initialize-at-build-time' can be used during image generation."); + return new ProvenSafeClassInitializationSupport(metaAccess, loader); } - return new ProvenSafeClassInitializationSupport(metaAccess, loader); + return new AllowAllHostedUsagesClassInitializationSupport(metaAccess, loader); } public static ClassInitializationSupport singleton() { diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java index d34bd7b0d50a..eb050f5d8d2c 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/image/DisallowedImageHeapObjectFeature.java @@ -168,11 +168,18 @@ private void checkDisallowedMBeanObjects(Object original) { } private RuntimeException error(String msg, Object obj, String initializerAction) { + String suffix = ""; + if (!ClassInitializationOptions.UseDeprecatedOldClassInitialization.getValue()) { + suffix = System.lineSeparator() + + "If you see this error while migrating to a newer GraalVM release, please note that the class initialization strategy has changed in GraalVM for JDK 21." + + " All classes can now be used at image build time. However, only classes explicitly marked as --initialize-at-built-time are allowed to be in the image heap." + + " This rule is now strictly enforced, i.e., the problem might be solvable by registering the reported type as --initialize-at-built-time."; + } throw new UnsupportedFeatureException(msg + " " + classInitialization.objectInstantiationTraceMessage(obj, initializerAction) + " " + "The object was probably created by a class initializer and is reachable from a static field. " + "You can request class initialization at image runtime by using the option " + SubstrateOptionsParser.commandArgument(ClassInitializationOptions.ClassInitialization, "", "initialize-at-run-time") + ". " + - "Or you can write your own initialization methods and call them explicitly from your main entry point."); + "Or you can write your own initialization methods and call them explicitly from your main entry point." + suffix); } private static boolean search(byte[] haystack, byte[] needle) { diff --git a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java index 92149d7ce60e..14f481166aa7 100644 --- a/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java +++ b/substratevm/src/com.oracle.svm.test/src/com/oracle/svm/test/clinit/TestClassInitialization.java @@ -714,8 +714,8 @@ public void afterImageWrite(AfterImageWriteAccess access) { } /** - * For testing with {@link ClassInitializationOptions#UseNewExperimentalClassInitialization} set to - * false and simulation of class initializer disabled. + * For testing with {@link ClassInitializationOptions#UseDeprecatedOldClassInitialization} set to + * true and simulation of class initializer disabled. */ class TestClassInitializationFeatureOldPolicyFeature extends TestClassInitializationFeature { @@ -752,8 +752,8 @@ void checkClass(Class checkedClass, boolean checkSafeEarly, boolean checkSafe } /** - * For testing with {@link ClassInitializationOptions#UseNewExperimentalClassInitialization} set to - * true and simulation of class initializer enabled. + * For testing with {@link ClassInitializationOptions#UseDeprecatedOldClassInitialization} set to + * false and simulation of class initializer enabled. */ class TestClassInitializationFeatureNewPolicyFeature extends TestClassInitializationFeature { @Override From f3114f981518b4e4a5a17e26ec6fd440d36e15bb Mon Sep 17 00:00:00 2001 From: Christian Wimmer Date: Mon, 17 Jul 2023 17:10:14 -0700 Subject: [PATCH 2/2] Remove special class initialization handling for lambda classes --- .../AllowAllHostedUsagesClassInitializationSupport.java | 6 ++---- .../classinitialization/ClassInitializationFeature.java | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/AllowAllHostedUsagesClassInitializationSupport.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/AllowAllHostedUsagesClassInitializationSupport.java index 2ccb82b10545..76058e96bccd 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/AllowAllHostedUsagesClassInitializationSupport.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/AllowAllHostedUsagesClassInitializationSupport.java @@ -26,8 +26,6 @@ import java.lang.reflect.Proxy; -import org.graalvm.compiler.java.LambdaUtils; - import com.oracle.graal.pointsto.meta.AnalysisMetaAccess; import com.oracle.graal.pointsto.meta.AnalysisUniverse; import com.oracle.svm.core.util.UserError; @@ -161,8 +159,8 @@ InitKind computeInitKindAndMaybeInitializeClass(Class clazz, boolean memoize) } superResult = superResult.max(processInterfaces(clazz, memoize)); - if (superResult == InitKind.BUILD_TIME && (Proxy.isProxyClass(clazz) || LambdaUtils.isLambdaType(metaAccess.lookupJavaType(clazz)))) { - forceInitializeHosted(clazz, "proxy/lambda classes with interfaces initialized at build time are also initialized at build time", false); + if (superResult == InitKind.BUILD_TIME && Proxy.isProxyClass(clazz)) { + forceInitializeHosted(clazz, "proxy classes with interfaces initialized at build time are also initialized at build time", false); return InitKind.BUILD_TIME; } diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java index 06720f9809fe..534365051c08 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/classinitialization/ClassInitializationFeature.java @@ -39,6 +39,7 @@ import org.graalvm.collections.Pair; import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.java.LambdaUtils; import org.graalvm.compiler.options.OptionValues; import org.graalvm.compiler.phases.util.Providers; import org.graalvm.nativeimage.impl.clinit.ClassInitializationTracking; @@ -207,6 +208,7 @@ public void afterAnalysis(AfterAnalysisAccess a) { .filter(c -> c.getClassLoader() != null && c.getClassLoader() != ClassLoader.getPlatformClassLoader()) .filter(c -> classInitializationSupport.specifiedInitKindFor(c) == null) .map(Class::getTypeName) + .filter(name -> !LambdaUtils.isLambdaName(name)) .collect(Collectors.toList()); if (!unspecifiedClasses.isEmpty()) { System.err.println("The following classes have unspecified initialization policy:" + System.lineSeparator() + String.join(System.lineSeparator(), unspecifiedClasses));