Skip to content

Commit ba06435

Browse files
committed
Flag for future default behavior of Native Image
1 parent 7de1286 commit ba06435

File tree

10 files changed

+176
-10
lines changed

10 files changed

+176
-10
lines changed

docs/reference-manual/native-image/BuildOutput.md

+6
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,12 @@ This feature is currently only available for code compiled by Graal for Linux AM
283283

284284
The build output may contain one or more of the following recommendations that help you get the best out of Native Image.
285285

286+
#### <a name="recommendation-futr"></a>`FUTR`: Use the Correct Semantics and Prepare for Future Releases
287+
288+
Use `--future-defaults=all` to enable all features that are planned to be default in a future GraalVM release.
289+
This option is unlikely to affect your program's behavior but guarantees that it adheres to the correct execution semantics.
290+
Additionally, it safeguards against unexpected changes in future GraalVM updates.
291+
286292
#### <a name="recommendation-awt"></a>`AWT`: Missing Reachability Metadata for Abstract Window Toolkit
287293

288294
The Native Image analysis has included classes from the [`java.awt` package](https://docs.oracle.com/en/java/javase/22/docs/api/java.desktop/java/awt/package-summary.html) but could not find any reachability metadata for it.

sdk/mx.sdk/mx_sdk_benchmark.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,8 @@ def __init__(self, vm: NativeImageVM, bm_suite: BenchmarkSuite | NativeImageBenc
408408
base_image_build_args += ['-H:Preserve=all']
409409
if vm.preserve_classpath:
410410
base_image_build_args += ['-H:Preserve=module=ALL-UNNAMED']
411+
if vm.future_defaults_all:
412+
base_image_build_args += ['--future-defaults=all']
411413
if vm.analysis_context_sensitivity:
412414
base_image_build_args += ['-H:AnalysisContextSensitivity=' + vm.analysis_context_sensitivity,
413415
'-H:-RemoveSaturatedTypeFlows', '-H:+AliasArrayTypeFlows']
@@ -719,6 +721,7 @@ def __init__(self, name, config_name, extra_java_args=None, extra_launcher_args=
719721
self.native_architecture = False
720722
self.preserve_all = False
721723
self.preserve_classpath = False
724+
self.future_defaults_all = False
722725
self.use_upx = False
723726
self.use_open_type_world = False
724727
self.use_compacting_gc = False
@@ -752,19 +755,22 @@ def canonical_config_name(config_name):
752755
def config_name(self):
753756
# Generates the unique vm config name based on how the VM is actually configured.
754757
# It concatenates the config options in the correct order to match the expected format.
758+
# Note: the order of entries here must match the order of entries in _configure_from_name
755759
config = []
756760
if self.native_architecture is True:
757761
config += ["native-architecture"]
758-
if self.preserve_all is True:
759-
config += ["preserve-all"]
760-
if self.preserve_classpath is True:
761-
config += ["preserve-classpath"]
762762
if self.use_string_inlining is True:
763763
config += ["string-inlining"]
764764
if self.use_open_type_world is True:
765765
config += ["otw"]
766766
if self.use_compacting_gc is True:
767767
config += ["compacting-gc"]
768+
if self.preserve_all is True:
769+
config += ["preserve-all"]
770+
if self.preserve_classpath is True:
771+
config += ["preserve-classpath"]
772+
if self.future_defaults_all is True:
773+
config += ["future-defaults-all"]
768774
if self.is_gate is True:
769775
config += ["gate"]
770776
if self.use_upx is True:
@@ -828,8 +834,9 @@ def _configure_from_name(self, config_name):
828834
mx.abort(f"config_name must be set. Use 'default' for the default {self.__class__.__name__} configuration.")
829835

830836
# This defines the allowed config names for NativeImageVM. The ones registered will be available via --jvm-config
837+
# Note: the order of entries here must match the order of statements in NativeImageVM.config_name()
831838
rule = r'^(?P<native_architecture>native-architecture-)?(?P<string_inlining>string-inlining-)?(?P<otw>otw-)?(?P<compacting_gc>compacting-gc-)?(?P<preserve_all>preserve-all-)?(?P<preserve_classpath>preserve-classpath-)?' \
832-
r'(?P<gate>gate-)?(?P<upx>upx-)?(?P<quickbuild>quickbuild-)?(?P<gc>g1gc-)?' \
839+
r'(?P<future_defaults_all>future-defaults-all-)?(?P<gate>gate-)?(?P<upx>upx-)?(?P<quickbuild>quickbuild-)?(?P<gc>g1gc-)?' \
833840
r'(?P<llvm>llvm-)?(?P<pgo>pgo-|pgo-sampler-)?(?P<inliner>inline-)?' \
834841
r'(?P<analysis_context_sensitivity>insens-|allocsens-|1obj-|2obj1h-|3obj2h-|4obj3h-)?(?P<no_inlining_before_analysis>no-inline-)?(?P<jdk_profiles>jdk-profiles-collect-|adopted-jdk-pgo-)?' \
835842
r'(?P<profile_inference>profile-inference-feature-extraction-|profile-inference-pgo-|profile-inference-debug-)?(?P<sampler>safepoint-sampler-|async-sampler-)?(?P<optimization_level>O0-|O1-|O2-|O3-|Os-)?(default-)?(?P<edition>ce-|ee-)?$'
@@ -852,6 +859,10 @@ def _configure_from_name(self, config_name):
852859
mx.logv(f"'preserve-classpath' is enabled for {config_name}")
853860
self.preserve_classpath = True
854861

862+
if matching.group("future_defaults_all") is not None:
863+
mx.logv(f"'future-defaults-all' is enabled for {config_name}")
864+
self.future_defaults_all = True
865+
855866
if matching.group("string_inlining") is not None:
856867
mx.logv(f"'string-inlining' is enabled for {config_name}")
857868
self.use_string_inlining = True

substratevm/CHANGELOG.md

+2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ This changelog summarizes major changes to GraalVM Native Image.
1111
* (GR-61492) The experimental JDWP option is now present in standard GraalVM builds.
1212
* (GR-55222) Enabled lazy deoptimization of runtime-compiled code, which reduces memory used for deoptimization. Can be turned off with `-H:-LazyDeoptimization`.
1313
* (GR-54953) Add the experimental option `-H:Preserve` that makes the program work correctly without providing reachability metadata. Correctness is achieved by preserving all classes, resources, and reflection metadata in the image. Usage: `-H:Preserve=[all|none|module=<module>|package=<package>|package=<package-wildcard>|path=<cp-entry>][,...]`.
14+
* (GR-49525) Introduced `--future-defaults=[all|<options>|none]` that enables options that are planned to become defaults in future releases. The enabled options are:
15+
1. `run-time-initialized-jdk` shifts away from build-time initialization of the JDK, instead initializing most of it at run time. This transition is gradual, with individual components of the JDK becoming run-time initialized in each release. This process should complete with JDK 29 when this option should not be needed anymore. Unless you store classes from the JDK in the image heap, this option should not affect you. In case this option breaks your build, follow the suggestions in the error messages.
1416

1517
## GraalVM for JDK 24 (Internal Version 24.2.0)
1618
* (GR-59717) Added `DuringSetupAccess.registerObjectReachabilityHandler` to allow registering a callback that is executed when an object of a specified type is marked as reachable during heap scanning.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
/*
2+
* Copyright (c) 2025, 2025, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core;
26+
27+
import java.util.Objects;
28+
import java.util.Set;
29+
30+
import org.graalvm.collections.EconomicSet;
31+
import org.graalvm.nativeimage.Platform;
32+
import org.graalvm.nativeimage.Platforms;
33+
34+
import com.oracle.svm.core.option.APIOption;
35+
import com.oracle.svm.core.option.AccumulatingLocatableMultiOptionValue;
36+
import com.oracle.svm.core.option.HostedOptionKey;
37+
import com.oracle.svm.core.option.SubstrateOptionsParser;
38+
import com.oracle.svm.core.util.UserError;
39+
import com.oracle.svm.util.StringUtil;
40+
41+
import jdk.graal.compiler.options.Option;
42+
import jdk.graal.compiler.options.OptionType;
43+
44+
/**
45+
* What makes it into the future-defaults must not be an experimental feature that can be rolled
46+
* back. The changes must be aligning native image with the Java spec and must be thoroughly
47+
* reviewed.
48+
*/
49+
public class FutureDefaultsOption {
50+
private static final String OPTION_NAME = "future-defaults";
51+
52+
private static final String DEFAULT_NAME = "<default-value>";
53+
private static final String ALL_NAME = "all";
54+
private static final String NONE_NAME = "none";
55+
private static final String RUN_TIME_INITIALIZE_JDK_NAME = "run-time-initialized-jdk";
56+
57+
private static final Set<String> ALL_VALUES = Set.of(RUN_TIME_INITIALIZE_JDK_NAME, ALL_NAME, NONE_NAME);
58+
59+
private static String futureDefaultsAllValues() {
60+
return StringUtil.joinSingleQuoted(ALL_VALUES);
61+
}
62+
63+
static {
64+
assert ALL_VALUES.stream().allMatch(futureDefaultsAllValues()::contains) : "A value is missing in the user-facing help text";
65+
}
66+
67+
@APIOption(name = OPTION_NAME, defaultValue = DEFAULT_NAME) //
68+
@Option(help = "file:doc-files/FutureDefaultsHelp.txt", type = OptionType.User) //
69+
public static final HostedOptionKey<AccumulatingLocatableMultiOptionValue.Strings> FutureDefaults = new HostedOptionKey<>(
70+
AccumulatingLocatableMultiOptionValue.Strings.buildWithCommaDelimiter());
71+
72+
private static EconomicSet<String> futureDefaults;
73+
74+
@Platforms(Platform.HOSTED_ONLY.class)
75+
public static void parseAndVerifyOption() {
76+
futureDefaults = EconomicSet.create(ALL_VALUES.size());
77+
var valuesWithOrigin = FutureDefaults.getValue().getValuesWithOrigins();
78+
valuesWithOrigin.forEach(valueWithOrigin -> {
79+
String value = valueWithOrigin.value();
80+
if (DEFAULT_NAME.equals(value)) {
81+
throw UserError.abort("The '%s' from %s is forbidden. It can only contain: %s.",
82+
SubstrateOptionsParser.commandArgument(FutureDefaults, DEFAULT_NAME),
83+
valueWithOrigin.origin(),
84+
futureDefaultsAllValues());
85+
}
86+
87+
if (!ALL_VALUES.contains(value)) {
88+
throw UserError.abort("The '%s' option from %s contains invalid value '%s'. It can only contain: %s.",
89+
SubstrateOptionsParser.commandArgument(FutureDefaults, value),
90+
valueWithOrigin.origin(),
91+
value,
92+
futureDefaultsAllValues());
93+
}
94+
95+
if (value.equals(NONE_NAME)) {
96+
if (!valueWithOrigin.origin().commandLineLike()) {
97+
throw UserError.abort("The '%s' option can only be used from the command line. Detected usage from %s.",
98+
SubstrateOptionsParser.commandArgument(FutureDefaults, NONE_NAME),
99+
valueWithOrigin.origin());
100+
}
101+
futureDefaults.clear();
102+
}
103+
104+
futureDefaults.add(value);
105+
});
106+
}
107+
108+
private static EconomicSet<String> getFutureDefaults() {
109+
return Objects.requireNonNull(futureDefaults, "must be initialized before usage");
110+
}
111+
112+
public static boolean allFutureDefaults() {
113+
return getFutureDefaults().contains(ALL_NAME);
114+
}
115+
116+
public static boolean isJDKInitializedAtRunTime() {
117+
return allFutureDefaults() || getFutureDefaults().contains(RUN_TIME_INITIALIZE_JDK_NAME);
118+
}
119+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
[[rule]]
2+
files = "FutureDefaultsOption.java"
3+
all = [
4+
5+
6+
]
7+
any = [
8+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Enable options that are planned to become defaults in future releases. Comma-separated list can contain 'all', 'run-time-initialized-jdk', 'none'.
2+
3+
The preferred way to use this option is '--future-defaults=all'. The meaning of each possible option is as follows:
4+
1. 'all' is the preferred option, and it enables all other behaviors.
5+
2. 'run-time-initialized-jdk' shifts away from build-time initialization of the JDK, instead initializing most of it at run time. This transition is gradual, with individual components of the JDK becoming run-time initialized in each release. This process should complete with JDK 29 when this option should not be needed anymore. Unless you store classes from the JDK in the image heap, this option should not affect you. In case this option breaks your build, follow the suggestions in the error messages.
6+
3. 'none' disables all previous behaviors in case they were added by someone else on the command line.

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/NativeImageGenerator.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
import java.util.function.BooleanSupplier;
5454
import java.util.function.Function;
5555

56+
import com.oracle.svm.core.FutureDefaultsOption;
5657
import org.graalvm.collections.EconomicSet;
5758
import org.graalvm.collections.Pair;
5859
import org.graalvm.nativeimage.ImageInfo;
@@ -919,7 +920,7 @@ protected void setupNativeImage(String imageName, OptionValues options, Map<Meth
919920

920921
ImageSingletons.add(SubstrateOptions.ReportingSupport.class, new SubstrateOptions.ReportingSupport(
921922
DiagnosticsMode.getValue() ? DiagnosticsDir.getValue().lastValue().get() : Path.of("reports")));
922-
923+
FutureDefaultsOption.parseAndVerifyOption();
923924
if (javaMainSupport != null) {
924925
ImageSingletons.add(JavaMainSupport.class, javaMainSupport);
925926
}

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/ProgressReporterFeature.java

+5
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,17 @@ public void createAdditionalArtifactsOnSuccess(@SuppressWarnings("unused") Build
106106

107107
protected List<UserRecommendation> getRecommendations() {
108108
return List.of(// in order of appearance:
109+
new UserRecommendation("FUTR", "Use '--future-defaults=all' to prepare for future releases.", ProgressReporterFeature::recommendFutureDefaults),
109110
new UserRecommendation("AWT", "Use the tracing agent to collect metadata for AWT.", ProgressReporterFeature::recommendTraceAgentForAWT),
110111
new UserRecommendation("HOME", "To avoid errors, provide java.home to the app with '-Djava.home=<path>'.", AnalyzeJavaHomeAccessFeature.instance()::getJavaHomeUsed),
111112
new UserRecommendation("HEAP", "Set max heap for improved and more predictable memory usage.", () -> SubstrateGCOptions.MaxHeapSize.getValue() == 0),
112113
new UserRecommendation("CPU", "Enable more CPU features with '-march=native' for improved performance.", ProgressReporterFeature::recommendMArchNative));
113114
}
114115

116+
private static boolean recommendFutureDefaults() {
117+
return !SubstrateOptions.allFutureDefaults();
118+
}
119+
115120
private static boolean recommendMArchNative() {
116121
if (NativeImageOptions.MicroArchitecture.getValue() != null) {
117122
return false; // explicitly set by user

substratevm/src/com.oracle.svm.hosted/src/com/oracle/svm/hosted/jdk/JDKInitializationFeature.java

+11-3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import java.lang.reflect.Field;
2828

29+
import com.oracle.svm.core.FutureDefaultsOption;
2930
import org.graalvm.nativeimage.ImageSingletons;
3031
import org.graalvm.nativeimage.Platform;
3132
import org.graalvm.nativeimage.impl.RuntimeClassInitializationSupport;
@@ -143,9 +144,16 @@ public void afterRegistration(AfterRegistrationAccess access) {
143144
rci.initializeAtBuildTime("java.awt.font.JavaAWTFontAccessImpl", "Required for sun.text.bidi.BidiBase.NumericShapings");
144145

145146
/* XML-related */
146-
rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
147-
rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
148-
rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);
147+
if (FutureDefaultsOption.isJDKInitializedAtRunTime()) {
148+
// GR-50683 should remove this part
149+
rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
150+
rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
151+
rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);
152+
} else {
153+
rci.initializeAtBuildTime("com.sun.xml", JDK_CLASS_REASON);
154+
rci.initializeAtBuildTime("com.sun.org.apache", JDK_CLASS_REASON);
155+
rci.initializeAtBuildTime("com.sun.org.slf4j.internal", JDK_CLASS_REASON);
156+
}
149157

150158
/* Security services */
151159
rci.initializeAtBuildTime("com.sun.crypto.provider", JDK_CLASS_REASON);

vm/mx.vm/mx_vm_benchmark.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ def register_graalvm_vms():
518518

519519
for short_name, config_suffix in [('niee', 'ee'), ('ni', 'ce')]:
520520
if any(component.short_name == short_name for component in mx_sdk_vm_impl.registered_graalvm_components(stage1=False)):
521-
for main_config in ['default', 'gate', 'llvm', 'native-architecture', 'preserve-all', 'preserve-classpath'] + analysis_context_sensitivity:
521+
for main_config in ['default', 'gate', 'llvm', 'native-architecture', 'future-defaults-all', 'preserve-all', 'preserve-classpath'] + analysis_context_sensitivity:
522522
final_config_name = f'{main_config}-{config_suffix}'
523523
mx_benchmark.add_java_vm(NativeImageVM('native-image', final_config_name, ['--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED']), _suite, 10)
524524
# ' ' force the empty O<> configs as well

0 commit comments

Comments
 (0)