Skip to content

Commit e163fd3

Browse files
author
Christian Wimmer
committed
Remove the old class initialization strategy
1 parent 0ffae37 commit e163fd3

26 files changed

+466
-1891
lines changed

sdk/src/org.graalvm.nativeimage/src/org/graalvm/nativeimage/impl/RuntimeClassInitializationSupport.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* The Universal Permissive License (UPL), Version 1.0
@@ -46,11 +46,25 @@ public interface RuntimeClassInitializationSupport {
4646

4747
void initializeAtBuildTime(String name, String reason);
4848

49-
void rerunInitialization(String name, String reason);
49+
@Deprecated
50+
default void rerunInitialization(String name, String reason) {
51+
/*
52+
* There is no more difference between initializing a class at run-time and re-running the
53+
* class initializer at run time.
54+
*/
55+
initializeAtRunTime(name, reason);
56+
}
5057

5158
void initializeAtRunTime(Class<?> aClass, String reason);
5259

53-
void rerunInitialization(Class<?> aClass, String reason);
60+
@Deprecated
61+
default void rerunInitialization(Class<?> aClass, String reason) {
62+
/*
63+
* There is no more difference between initializing a class at run-time and re-running the
64+
* class initializer at run time.
65+
*/
66+
initializeAtRunTime(aClass, reason);
67+
}
5468

5569
void initializeAtBuildTime(Class<?> aClass, String reason);
5670
}

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
This changelog summarizes major changes to GraalVM Native Image.
44

55
## GraalVM for JDK 23 (Internal Version 24.1.0)
6+
* (GR-51520) The old class initialization strategy, which was deprecated in GraalVM for JDK 22, is removed. The option `StrictImageHeap` no longer has any effect.
67
* (GR-51106) Fields that are accessed via a `VarHandle` or `MethodHandle` are no longer marked as "unsafe accessed" when the `VarHandle`/`MethodHandle` can be fully intrinsified.
78
* (GR-49996) Ensure explicitly set image name (e.g., via `-o imagename`) is not accidentally overwritten by `-jar jarfile` option.
89
* (GR-48683) Together with Red Hat, we added partial support for the JFR event `OldObjectSample`.

substratevm/mx.substratevm/mx_substratevm.py

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,11 +1549,7 @@ def cinterfacetutorial(args):
15491549

15501550
@mx.command(suite.name, 'clinittest', 'Runs the ')
15511551
def clinittest(args):
1552-
def build_and_test_clinittest_images(native_image, args=None):
1553-
build_and_test_clinittest_image(native_image, args, True)
1554-
build_and_test_clinittest_image(native_image, args, False)
1555-
1556-
def build_and_test_clinittest_image(native_image, args, new_class_init_policy):
1552+
def build_and_test_clinittest_image(native_image, args):
15571553
args = [] if args is None else args
15581554
test_cp = classpath('com.oracle.svm.test')
15591555
build_dir = join(svmbuild_dir(), 'clinittest')
@@ -1563,11 +1559,6 @@ def build_and_test_clinittest_image(native_image, args, new_class_init_policy):
15631559
mx.rmtree(build_dir)
15641560
mx.ensure_dir_exists(build_dir)
15651561

1566-
if new_class_init_policy:
1567-
policy_args = svm_experimental_options(['-H:+SimulateClassInitializer']) + ['--features=com.oracle.svm.test.clinit.TestClassInitializationFeatureNewPolicyFeature']
1568-
else:
1569-
policy_args = svm_experimental_options(['-H:-StrictImageHeap', '-H:-SimulateClassInitializer']) + ['--features=com.oracle.svm.test.clinit.TestClassInitializationFeatureOldPolicyFeature']
1570-
15711562
# Build and run the example
15721563
binary_path = join(build_dir, 'clinittest')
15731564
native_image([
@@ -1577,9 +1568,10 @@ def build_and_test_clinittest_image(native_image, args, new_class_init_policy):
15771568
'-o', binary_path,
15781569
'-H:+ReportExceptionStackTraces',
15791570
'-H:Class=com.oracle.svm.test.clinit.TestClassInitialization',
1571+
'--features=com.oracle.svm.test.clinit.TestClassInitializationFeature',
15801572
] + svm_experimental_options([
15811573
'-H:+PrintClassInitialization',
1582-
]) + policy_args + args)
1574+
]) + args)
15831575
mx.run([binary_path])
15841576

15851577
# Check the reports for initialized classes
@@ -1593,16 +1585,8 @@ def checkLine(line, marker, init_kind, msg, wrongly_initialized_lines):
15931585
"Classes marked with " + marker + " must have init kind " + init_kind + " and message " + msg)]
15941586
with open(classes_file) as f:
15951587
for line in f:
1596-
if new_class_init_policy:
1597-
checkLine(line, "MustBeSafeEarly", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
1598-
checkLine(line, "MustBeSafeLate", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
1599-
checkLine(line, "MustBeSimulated", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
1600-
checkLine(line, "MustBeDelayed", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
1601-
else:
1602-
checkLine(line, "MustBeSafeEarly", "BUILD_TIME", "class proven as side-effect free before analysis", wrongly_initialized_lines)
1603-
checkLine(line, "MustBeSafeLate", "BUILD_TIME", "class proven as side-effect free after analysis", wrongly_initialized_lines)
1604-
checkLine(line, "MustBeSimulated", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
1605-
checkLine(line, "MustBeDelayed", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
1588+
checkLine(line, "MustBeSimulated", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
1589+
checkLine(line, "MustBeDelayed", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
16061590

16071591
if len(wrongly_initialized_lines) > 0:
16081592
msg = ""
@@ -1615,7 +1599,7 @@ def checkLine(line, marker, init_kind, msg, wrongly_initialized_lines):
16151599

16161600
check_class_initialization(all_classes_file)
16171601

1618-
native_image_context_run(build_and_test_clinittest_images, args)
1602+
native_image_context_run(build_and_test_clinittest_image, args)
16191603

16201604

16211605
class SubstrateJvmFuncsFallbacksBuilder(mx.Project):

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JNIRegistrationUtil.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ protected static boolean isWindows() {
6565
return Platform.includedIn(Platform.WINDOWS.class);
6666
}
6767

68-
protected static void rerunClassInit(FeatureAccess access, String... classNames) {
68+
protected static void initializeAtRunTime(FeatureAccess access, String... classNames) {
6969
RuntimeClassInitializationSupport classInitSupport = ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
7070
for (String className : classNames) {
71-
classInitSupport.rerunInitialization(clazz(access, className), "for JDK native code support via JNI");
71+
classInitSupport.initializeAtRunTime(clazz(access, className), "for JDK native code support via JNI");
7272
}
7373
}
7474

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/JavaNetHttpFeature.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public void afterRegistration(AfterRegistrationAccess access) {
5656
public void duringSetup(DuringSetupAccess access) {
5757
RuntimeClassInitializationSupport rci = ImageSingletons.lookup(RuntimeClassInitializationSupport.class);
5858
rci.initializeAtRunTime("jdk.internal.net.http", "for reading properties at run time");
59-
rci.rerunInitialization("jdk.internal.net.http.websocket.OpeningHandshake", "contains a SecureRandom reference");
59+
rci.initializeAtRunTime("jdk.internal.net.http.websocket.OpeningHandshake", "contains a SecureRandom reference");
6060
}
6161

6262
@Override

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jdk/RecomputedFields.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ private void processFieldUpdater(Object updater) {
296296
class InnocuousForkJoinWorkerThreadFeature implements InternalFeature {
297297
@Override
298298
public void duringSetup(DuringSetupAccess access) {
299-
ImageSingletons.lookup(RuntimeClassInitializationSupport.class).rerunInitialization(access.findClassByName("java.util.concurrent.ForkJoinWorkerThread$InnocuousForkJoinWorkerThread"),
299+
ImageSingletons.lookup(RuntimeClassInitializationSupport.class).initializeAtRunTime(access.findClassByName("java.util.concurrent.ForkJoinWorkerThread$InnocuousForkJoinWorkerThread"),
300300
"innocuousThreadGroup must be initialized at run time");
301301
}
302302
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -896,7 +896,7 @@ protected void setupNativeImage(OptionValues options, Map<Method, CEntryPointDat
896896
ImageSingletons.add(LinkAtBuildTimeSupport.class, new LinkAtBuildTimeSupport(loader, classLoaderSupport));
897897
ImageSingletons.add(ObservableImageHeapMapProvider.class, new ObservableImageHeapMapProviderImpl());
898898

899-
ClassInitializationSupport classInitializationSupport = ClassInitializationSupport.create(originalMetaAccess, loader);
899+
ClassInitializationSupport classInitializationSupport = new ClassInitializationSupport(originalMetaAccess, loader);
900900
ImageSingletons.add(RuntimeClassInitializationSupport.class, classInitializationSupport);
901901
ClassInitializationFeature.processClassInitializationOptions(classInitializationSupport);
902902

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

Lines changed: 0 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,9 @@
3333
import java.lang.reflect.Method;
3434
import java.lang.reflect.Modifier;
3535
import java.lang.reflect.Proxy;
36-
import java.util.Collections;
3736
import java.util.Comparator;
3837
import java.util.EnumSet;
3938
import java.util.HashMap;
40-
import java.util.HashSet;
4139
import java.util.List;
4240
import java.util.Map;
4341
import java.util.Optional;
@@ -77,11 +75,9 @@
7775
import com.oracle.svm.core.SubstrateOptions.OptimizationLevel;
7876
import com.oracle.svm.core.annotate.InjectAccessors;
7977
import com.oracle.svm.core.c.CGlobalData;
80-
import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode;
8178
import com.oracle.svm.core.graal.meta.SubstrateForeignCallLinkage;
8279
import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider;
8380
import com.oracle.svm.core.graal.stackvalue.StackValueNode;
84-
import com.oracle.svm.core.graal.thread.VMThreadLocalAccess;
8581
import com.oracle.svm.core.heap.StoredContinuation;
8682
import com.oracle.svm.core.heap.Target_java_lang_ref_Reference;
8783
import com.oracle.svm.core.heap.UnknownClass;
@@ -131,11 +127,8 @@
131127
import jdk.graal.compiler.nodes.StaticDeoptimizingNode;
132128
import jdk.graal.compiler.nodes.StructuredGraph;
133129
import jdk.graal.compiler.nodes.ValueNode;
134-
import jdk.graal.compiler.nodes.extended.UnsafeAccessNode;
135130
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
136131
import jdk.graal.compiler.nodes.graphbuilderconf.IntrinsicContext;
137-
import jdk.graal.compiler.nodes.java.AccessFieldNode;
138-
import jdk.graal.compiler.nodes.java.AccessMonitorNode;
139132
import jdk.graal.compiler.options.Option;
140133
import jdk.graal.compiler.options.OptionKey;
141134
import jdk.graal.compiler.options.OptionValues;
@@ -171,8 +164,6 @@ public class SVMHost extends HostVM {
171164
* need to keep the whole graphs alive.
172165
*/
173166
private final ConcurrentMap<AnalysisMethod, Boolean> containsStackValueNode = new ConcurrentHashMap<>();
174-
private final ConcurrentMap<AnalysisMethod, Boolean> classInitializerSideEffect = new ConcurrentHashMap<>();
175-
private final ConcurrentMap<AnalysisMethod, Set<AnalysisType>> initializedClasses = new ConcurrentHashMap<>();
176167
private final ConcurrentMap<AnalysisMethod, Boolean> analysisTrivialMethods = new ConcurrentHashMap<>();
177168

178169
private final Set<AnalysisField> finalFieldsInitializedOutsideOfConstructor = ConcurrentHashMap.newKeySet();
@@ -649,53 +640,6 @@ public void methodBeforeTypeFlowCreationHook(BigBang bb, AnalysisMethod method,
649640
} else if (n instanceof ReachabilityRegistrationNode node) {
650641
bb.postTask(debug -> node.getRegistrationTask().ensureDone());
651642
}
652-
checkClassInitializerSideEffect(method, n);
653-
}
654-
}
655-
656-
/**
657-
* Classes are only safe for automatic initialization if the class initializer has no side
658-
* effect on other classes and cannot be influenced by other classes. Otherwise there would be
659-
* observable side effects. For example, if a class initializer of class A writes a static field
660-
* B.f in class B, then someone could rely on reading the old value of B.f before triggering
661-
* initialization of A. Similarly, if a class initializer of class A reads a static field B.f,
662-
* then an early automatic initialization of class A could read a non-yet-set value of B.f.
663-
*
664-
* Note that it is not necessary to disallow instance field accesses: Objects allocated by the
665-
* class initializer itself can always be accessed because they are independent from other
666-
* initializers; all other objects must be loaded transitively from a static field.
667-
*
668-
* Currently, we are conservative and mark all methods that access static fields as unsafe for
669-
* automatic class initialization (unless the class initializer itself accesses a static field
670-
* of its own class - the common way of initializing static fields). The check could be relaxed
671-
* by tracking the call chain, i.e., allowing static field accesses when the root method of the
672-
* call chain is the class initializer. But this does not fit well into the current approach
673-
* where each method has a `Safety` flag.
674-
*/
675-
private void checkClassInitializerSideEffect(AnalysisMethod method, Node n) {
676-
if (n instanceof AccessFieldNode) {
677-
ResolvedJavaField field = ((AccessFieldNode) n).field();
678-
if (field.isStatic() && (!method.isClassInitializer() || !field.getDeclaringClass().equals(method.getDeclaringClass()))) {
679-
classInitializerSideEffect.put(method, true);
680-
}
681-
} else if (n instanceof UnsafeAccessNode || n instanceof VMThreadLocalAccess) {
682-
/*
683-
* Unsafe memory access nodes are rare, so it does not pay off to check what kind of
684-
* field they are accessing.
685-
*
686-
* Methods that access a thread-local value cannot be initialized at image build time
687-
* because such values are not available yet.
688-
*/
689-
classInitializerSideEffect.put(method, true);
690-
} else if (n instanceof EnsureClassInitializedNode) {
691-
ResolvedJavaType type = ((EnsureClassInitializedNode) n).constantTypeOrNull(getProviders(method.getMultiMethodKey()).getConstantReflection());
692-
if (type != null) {
693-
initializedClasses.computeIfAbsent(method, k -> new HashSet<>()).add((AnalysisType) type);
694-
} else {
695-
classInitializerSideEffect.put(method, true);
696-
}
697-
} else if (n instanceof AccessMonitorNode) {
698-
classInitializerSideEffect.put(method, true);
699643
}
700644
}
701645

@@ -714,19 +658,6 @@ public boolean containsStackValueNode(AnalysisMethod method) {
714658
return containsStackValueNode.containsKey(method);
715659
}
716660

717-
public boolean hasClassInitializerSideEffect(AnalysisMethod method) {
718-
return classInitializerSideEffect.containsKey(method);
719-
}
720-
721-
public Set<AnalysisType> getInitializedClasses(AnalysisMethod method) {
722-
Set<AnalysisType> result = initializedClasses.get(method);
723-
if (result != null) {
724-
return result;
725-
} else {
726-
return Collections.emptySet();
727-
}
728-
}
729-
730661
public boolean isAnalysisTrivialMethod(AnalysisMethod method) {
731662
return analysisTrivialMethods.containsKey(method);
732663
}

0 commit comments

Comments
 (0)