Skip to content

Commit f035c26

Browse files
author
Christian Wimmer
committed
Remove the old class initialization strategy
1 parent b53e143 commit f035c26

26 files changed

+467
-1892
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
@@ -1547,11 +1547,7 @@ def cinterfacetutorial(args):
15471547

15481548
@mx.command(suite.name, 'clinittest', 'Runs the ')
15491549
def clinittest(args):
1550-
def build_and_test_clinittest_images(native_image, args=None):
1551-
build_and_test_clinittest_image(native_image, args, True)
1552-
build_and_test_clinittest_image(native_image, args, False)
1553-
1554-
def build_and_test_clinittest_image(native_image, args, new_class_init_policy):
1550+
def build_and_test_clinittest_image(native_image, args):
15551551
args = [] if args is None else args
15561552
test_cp = classpath('com.oracle.svm.test')
15571553
build_dir = join(svmbuild_dir(), 'clinittest')
@@ -1561,11 +1557,6 @@ def build_and_test_clinittest_image(native_image, args, new_class_init_policy):
15611557
mx.rmtree(build_dir)
15621558
mx.ensure_dir_exists(build_dir)
15631559

1564-
if new_class_init_policy:
1565-
policy_args = svm_experimental_options(['-H:+SimulateClassInitializer']) + ['--features=com.oracle.svm.test.clinit.TestClassInitializationFeatureNewPolicyFeature']
1566-
else:
1567-
policy_args = svm_experimental_options(['-H:-StrictImageHeap', '-H:-SimulateClassInitializer']) + ['--features=com.oracle.svm.test.clinit.TestClassInitializationFeatureOldPolicyFeature']
1568-
15691560
# Build and run the example
15701561
binary_path = join(build_dir, 'clinittest')
15711562
native_image([
@@ -1575,9 +1566,10 @@ def build_and_test_clinittest_image(native_image, args, new_class_init_policy):
15751566
'-o', binary_path,
15761567
'-H:+ReportExceptionStackTraces',
15771568
'-H:Class=com.oracle.svm.test.clinit.TestClassInitialization',
1569+
'--features=com.oracle.svm.test.clinit.TestClassInitializationFeature',
15781570
] + svm_experimental_options([
15791571
'-H:+PrintClassInitialization',
1580-
]) + policy_args + args)
1572+
]) + args)
15811573
mx.run([binary_path])
15821574

15831575
# Check the reports for initialized classes
@@ -1591,16 +1583,8 @@ def checkLine(line, marker, init_kind, msg, wrongly_initialized_lines):
15911583
"Classes marked with " + marker + " must have init kind " + init_kind + " and message " + msg)]
15921584
with open(classes_file) as f:
15931585
for line in f:
1594-
if new_class_init_policy:
1595-
checkLine(line, "MustBeSafeEarly", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
1596-
checkLine(line, "MustBeSafeLate", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
1597-
checkLine(line, "MustBeSimulated", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
1598-
checkLine(line, "MustBeDelayed", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
1599-
else:
1600-
checkLine(line, "MustBeSafeEarly", "BUILD_TIME", "class proven as side-effect free before analysis", wrongly_initialized_lines)
1601-
checkLine(line, "MustBeSafeLate", "BUILD_TIME", "class proven as side-effect free after analysis", wrongly_initialized_lines)
1602-
checkLine(line, "MustBeSimulated", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
1603-
checkLine(line, "MustBeDelayed", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
1586+
checkLine(line, "MustBeSimulated", "SIMULATED", "classes are initialized at run time by default", wrongly_initialized_lines)
1587+
checkLine(line, "MustBeDelayed", "RUN_TIME", "classes are initialized at run time by default", wrongly_initialized_lines)
16041588

16051589
if len(wrongly_initialized_lines) > 0:
16061590
msg = ""
@@ -1613,7 +1597,7 @@ def checkLine(line, marker, init_kind, msg, wrongly_initialized_lines):
16131597

16141598
check_class_initialization(all_classes_file)
16151599

1616-
native_image_context_run(build_and_test_clinittest_images, args)
1600+
native_image_context_run(build_and_test_clinittest_image, args)
16171601

16181602

16191603
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
@@ -894,7 +894,7 @@ protected void setupNativeImage(OptionValues options, Map<Method, CEntryPointDat
894894
ImageSingletons.add(LinkAtBuildTimeSupport.class, new LinkAtBuildTimeSupport(loader, classLoaderSupport));
895895
ImageSingletons.add(ObservableImageHeapMapProvider.class, new ObservableImageHeapMapProviderImpl());
896896

897-
ClassInitializationSupport classInitializationSupport = ClassInitializationSupport.create(originalMetaAccess, loader);
897+
ClassInitializationSupport classInitializationSupport = new ClassInitializationSupport(originalMetaAccess, loader);
898898
ImageSingletons.add(RuntimeClassInitializationSupport.class, classInitializationSupport);
899899
ClassInitializationFeature.processClassInitializationOptions(classInitializationSupport);
900900

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
@@ -32,11 +32,9 @@
3232
import java.lang.reflect.InvocationTargetException;
3333
import java.lang.reflect.Method;
3434
import java.lang.reflect.Modifier;
35-
import java.util.Collections;
3635
import java.util.Comparator;
3736
import java.util.EnumSet;
3837
import java.util.HashMap;
39-
import java.util.HashSet;
4038
import java.util.List;
4139
import java.util.Map;
4240
import java.util.Optional;
@@ -76,11 +74,9 @@
7674
import com.oracle.svm.core.SubstrateOptions.OptimizationLevel;
7775
import com.oracle.svm.core.annotate.InjectAccessors;
7876
import com.oracle.svm.core.c.CGlobalData;
79-
import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode;
8077
import com.oracle.svm.core.graal.meta.SubstrateForeignCallLinkage;
8178
import com.oracle.svm.core.graal.meta.SubstrateForeignCallsProvider;
8279
import com.oracle.svm.core.graal.stackvalue.StackValueNode;
83-
import com.oracle.svm.core.graal.thread.VMThreadLocalAccess;
8480
import com.oracle.svm.core.heap.StoredContinuation;
8581
import com.oracle.svm.core.heap.Target_java_lang_ref_Reference;
8682
import com.oracle.svm.core.heap.UnknownClass;
@@ -130,11 +126,8 @@
130126
import jdk.graal.compiler.nodes.StaticDeoptimizingNode;
131127
import jdk.graal.compiler.nodes.StructuredGraph;
132128
import jdk.graal.compiler.nodes.ValueNode;
133-
import jdk.graal.compiler.nodes.extended.UnsafeAccessNode;
134129
import jdk.graal.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
135130
import jdk.graal.compiler.nodes.graphbuilderconf.IntrinsicContext;
136-
import jdk.graal.compiler.nodes.java.AccessFieldNode;
137-
import jdk.graal.compiler.nodes.java.AccessMonitorNode;
138131
import jdk.graal.compiler.options.Option;
139132
import jdk.graal.compiler.options.OptionKey;
140133
import jdk.graal.compiler.options.OptionValues;
@@ -170,8 +163,6 @@ public class SVMHost extends HostVM {
170163
* need to keep the whole graphs alive.
171164
*/
172165
private final ConcurrentMap<AnalysisMethod, Boolean> containsStackValueNode = new ConcurrentHashMap<>();
173-
private final ConcurrentMap<AnalysisMethod, Boolean> classInitializerSideEffect = new ConcurrentHashMap<>();
174-
private final ConcurrentMap<AnalysisMethod, Set<AnalysisType>> initializedClasses = new ConcurrentHashMap<>();
175166
private final ConcurrentMap<AnalysisMethod, Boolean> analysisTrivialMethods = new ConcurrentHashMap<>();
176167

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

@@ -707,19 +651,6 @@ public boolean containsStackValueNode(AnalysisMethod method) {
707651
return containsStackValueNode.containsKey(method);
708652
}
709653

710-
public boolean hasClassInitializerSideEffect(AnalysisMethod method) {
711-
return classInitializerSideEffect.containsKey(method);
712-
}
713-
714-
public Set<AnalysisType> getInitializedClasses(AnalysisMethod method) {
715-
Set<AnalysisType> result = initializedClasses.get(method);
716-
if (result != null) {
717-
return result;
718-
} else {
719-
return Collections.emptySet();
720-
}
721-
}
722-
723654
public boolean isAnalysisTrivialMethod(AnalysisMethod method) {
724655
return analysisTrivialMethods.containsKey(method);
725656
}

0 commit comments

Comments
 (0)