From a90687ff896293f0d385e8008a1d226a5792e7cc Mon Sep 17 00:00:00 2001 From: Vojin Jovanovic Date: Fri, 8 Sep 2023 21:33:33 +0200 Subject: [PATCH] Flip the class initialization flag --- docs/reference-manual/native-image/BuildOutput.md | 9 +++++++++ substratevm/CHANGELOG.md | 2 +- substratevm/mx.substratevm/mx_substratevm.py | 4 ++-- .../svm/hosted/ProgressReporterFeature.java | 11 +++++++++-- .../ClassInitializationFeature.java | 15 +++++++++------ .../ClassInitializationOptions.java | 11 ++++++++--- .../ClassInitializationSupport.java | 4 +--- .../svm/test/clinit/TestClassInitialization.java | 8 ++++---- 8 files changed, 43 insertions(+), 21 deletions(-) diff --git a/docs/reference-manual/native-image/BuildOutput.md b/docs/reference-manual/native-image/BuildOutput.md index 7705cf93c66e..5feb69774703 100644 --- a/docs/reference-manual/native-image/BuildOutput.md +++ b/docs/reference-manual/native-image/BuildOutput.md @@ -258,6 +258,15 @@ This feature is currently only available for Linux AArch64 and leverages pointer The build output may contain one or more of the following recommendations that help you get the best out of Native Image. +#### `INIT`: Use the Strict Image Heap Configuration + +Start using `--strict-image-heap` to reduce the amount of configuration and prepare for future GraalVM releases where this will be the default. +This mode requires only the classes that are stored in the image heap to be marked with `--initialize-at-build-time`. +This effectively reduces the number of configuration entries necessary to achieve build-time initialization. +When adopting the new mode it is best to start introducing build-time initialization from scratch. +During this process, it is best to select individual classes (as opposed to whole packages) for build time initialization. +Also, before migrating to the new flag make sure to update all framework dependencies to the latest versions as they might need to migrate too. + #### `AWT`: Missing Reachability Metadata for Abstract Window Toolkit The Native Image analysis has included classes from the [`java.awt` package](https://docs.oracle.com/en/java/javase/17/docs/api/java.desktop/java/awt/package-summary.html) but could not find any reachability metadata for it. diff --git a/substratevm/CHANGELOG.md b/substratevm/CHANGELOG.md index fee70eb13569..afbb8f070326 100644 --- a/substratevm/CHANGELOG.md +++ b/substratevm/CHANGELOG.md @@ -10,7 +10,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-39406) New option `--strict-image-heap`: 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. Adopt this option as it will become the default in the next release of GraalVM. * (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. * (GR-38994) Together with Red Hat, we added support for `-XX:+HeapDumpOnOutOfMemoryError`. diff --git a/substratevm/mx.substratevm/mx_substratevm.py b/substratevm/mx.substratevm/mx_substratevm.py index 3351c5bb86f0..1bdcc849370e 100644 --- a/substratevm/mx.substratevm/mx_substratevm.py +++ b/substratevm/mx.substratevm/mx_substratevm.py @@ -1514,9 +1514,9 @@ def build_and_test_clinittest_image(native_image, args, new_class_init_policy): mx.ensure_dir_exists(build_dir) if new_class_init_policy: - policy_args = svm_experimental_options(['-H:-UseDeprecatedOldClassInitialization', '-H:+SimulateClassInitializer']) + ['--features=com.oracle.svm.test.clinit.TestClassInitializationFeatureNewPolicyFeature'] + policy_args = svm_experimental_options(['-H:+StrictImageHeap', '-H:+SimulateClassInitializer']) + ['--features=com.oracle.svm.test.clinit.TestClassInitializationFeatureNewPolicyFeature'] else: - policy_args = svm_experimental_options(['-H:+UseDeprecatedOldClassInitialization', '-H:-SimulateClassInitializer']) + ['--features=com.oracle.svm.test.clinit.TestClassInitializationFeatureOldPolicyFeature'] + policy_args = svm_experimental_options(['-H:-SimulateClassInitializer']) + ['--features=com.oracle.svm.test.clinit.TestClassInitializationFeatureOldPolicyFeature'] # Build and run the example binary_path = join(build_dir, 'clinittest') diff --git a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporterFeature.java b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporterFeature.java index 86b07922a2f5..12dfc270935a 100644 --- a/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporterFeature.java +++ b/substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporterFeature.java @@ -27,7 +27,6 @@ import java.util.List; import java.util.function.Supplier; -import com.oracle.svm.util.LogUtils; import org.graalvm.nativeimage.ImageSingletons; import com.oracle.svm.core.BuildArtifacts; @@ -38,12 +37,15 @@ import com.oracle.svm.core.feature.InternalFeature; import com.oracle.svm.core.jni.access.JNIAccessibleClass; import com.oracle.svm.core.jni.access.JNIReflectionDictionary; +import com.oracle.svm.core.option.SubstrateOptionsParser; import com.oracle.svm.hosted.FeatureImpl.AfterCompilationAccessImpl; import com.oracle.svm.hosted.FeatureImpl.BeforeImageWriteAccessImpl; import com.oracle.svm.hosted.ProgressReporter.DirectPrinter; +import com.oracle.svm.hosted.classinitialization.ClassInitializationOptions; import com.oracle.svm.hosted.jdk.JNIRegistrationSupport; import com.oracle.svm.hosted.util.CPUTypeAArch64; import com.oracle.svm.hosted.util.CPUTypeAMD64; +import com.oracle.svm.util.LogUtils; @AutomaticallyRegisteredFeature public class ProgressReporterFeature implements InternalFeature { @@ -90,6 +92,10 @@ public void createAdditionalArtifacts(@SuppressWarnings("unused") BuildArtifacts protected List getRecommendations() { return List.of(// in order of appearance: + new UserRecommendation("INIT", + "Adopt " + SubstrateOptionsParser.commandArgument(ClassInitializationOptions.StrictImageHeap, "+", "strict-initial-heap", true, false) + + " to prepare for the next GraalVM release.", + () -> !ClassInitializationOptions.StrictImageHeap.getValue()), new UserRecommendation("AWT", "Use the tracing agent to collect metadata for AWT.", ProgressReporterFeature::recommendTraceAgentForAWT), new UserRecommendation("HEAP", "Set max heap for improved and more predictable memory usage.", () -> SubstrateGCOptions.MaxHeapSize.getValue() == 0), new UserRecommendation("CPU", "Enable more CPU features with '-march=native' for improved performance.", ProgressReporterFeature::recommendMArchNative)); @@ -126,7 +132,8 @@ private static boolean recommendTraceAgentForAWT() { public record UserRecommendation(String id, String description, Supplier isApplicable) { public UserRecommendation(String id, String description, Supplier isApplicable) { assert id.toUpperCase().equals(id) && id.length() < 5 : "id must be uppercase and have fewer than 5 chars"; - assert description.length() < 74 : "description must have fewer than 74 chars to fit in terminal"; + int maxLength = 74; + assert description.length() < maxLength : "description must have fewer than " + maxLength + " chars to fit in terminal. Length: " + description.length(); this.id = id; this.description = description; this.isApplicable = isApplicable; 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 766999f8de04..88bb33967630 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 @@ -168,13 +168,16 @@ private Object checkImageHeapInstance(Object obj) { } }); - if (!ClassInitializationOptions.UseDeprecatedOldClassInitialization.getValue()) { + if (ClassInitializationOptions.StrictImageHeap.getValue()) { msg += """ - - 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-build-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-build-time. - """.replaceAll("\n", System.lineSeparator()); + If you are seeing this message after enabling %s, this means that some objects ended up in the image heap without their type being marked with --initialize-at-build-time. + To fix this, include %s in your configuration. If the classes do not originate from your code, it is advised to update all library or framework dependencies to the latest version before addressing this error. + Please address this problem to be prepared for future releases of GraalVM. + """ + .replaceAll("\n", System.lineSeparator()) + .formatted( + SubstrateOptionsParser.commandArgument(ClassInitializationOptions.StrictImageHeap, typeName, "strict-image-heap", true, false), + SubstrateOptionsParser.commandArgument(ClassInitializationOptions.ClassInitialization, typeName, "initialize-at-build-time", true, false)); } msg += System.lineSeparator() + "The following detailed trace displays from which field in the code the object was reached."; 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 54fca49082cd..4b2b30c2823c 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 @@ -37,6 +37,7 @@ import com.oracle.svm.core.option.APIOption; import com.oracle.svm.core.option.HostedOptionKey; import com.oracle.svm.core.option.LocatableMultiOptionValue; +import com.oracle.svm.core.util.UserError; public final class ClassInitializationOptions { @@ -100,9 +101,13 @@ 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 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); + @APIOption(name = "strict-image-heap")// + @Option(help = "Enable the strict image heap mode that allows all classes to be used at build-time but also requires types of all objects in the heap to be explicitly marked for build-time initialization.", type = OptionType.User) // + public static final HostedOptionKey StrictImageHeap = new HostedOptionKey<>(false, k -> { + if (k.hasBeenSet() && Boolean.FALSE.equals(k.getValue())) { + throw UserError.abort("Strict image heap mode cannot be explicitly disabled."); + } + }); @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 ed45549d9bc2..d310bf77fd57 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 @@ -52,7 +52,6 @@ import com.oracle.svm.core.util.UserError; import com.oracle.svm.hosted.ImageClassLoader; import com.oracle.svm.hosted.LinkAtBuildTimeSupport; -import com.oracle.svm.util.LogUtils; import jdk.internal.misc.Unsafe; import jdk.vm.ci.meta.MetaAccessProvider; @@ -89,8 +88,7 @@ public abstract class ClassInitializationSupport implements RuntimeClassInitiali final MetaAccessProvider metaAccess; public static ClassInitializationSupport create(MetaAccessProvider metaAccess, ImageClassLoader 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."); + if (!ClassInitializationOptions.StrictImageHeap.getValue()) { return new ProvenSafeClassInitializationSupport(metaAccess, loader); } return new AllowAllHostedUsagesClassInitializationSupport(metaAccess, loader); 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 ef00d6926740..ffa6fad54a78 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 @@ -727,8 +727,8 @@ public void afterImageWrite(AfterImageWriteAccess access) { } /** - * For testing with {@link ClassInitializationOptions#UseDeprecatedOldClassInitialization} set to - * true and simulation of class initializer disabled. + * For testing with {@link ClassInitializationOptions#StrictImageHeap} set to false and simulation + * of class initializer disabled. */ class TestClassInitializationFeatureOldPolicyFeature extends TestClassInitializationFeature { @@ -765,8 +765,8 @@ void checkClass(Class checkedClass, boolean checkSafeEarly, boolean checkSafe } /** - * For testing with {@link ClassInitializationOptions#UseDeprecatedOldClassInitialization} set to - * false and simulation of class initializer enabled. + * For testing with {@link ClassInitializationOptions#StrictImageHeap} set to true and simulation of + * class initializer enabled. */ class TestClassInitializationFeatureNewPolicyFeature extends TestClassInitializationFeature { @Override