2525package com .oracle .svm .core .jdk ;
2626
2727import java .util .Map ;
28- import java .util .concurrent .atomic .AtomicInteger ;
2928
3029import com .oracle .svm .core .NeverInline ;
3130import com .oracle .svm .core .SubstrateOptions ;
3837import com .oracle .svm .core .annotate .TargetClass ;
3938import com .oracle .svm .core .heap .PhysicalMemory ;
4039import com .oracle .svm .core .snippets .KnownIntrinsics ;
41- import com .oracle .svm .core .util .VMError ;
4240
4341import jdk .internal .misc .Unsafe ;
4442
@@ -72,44 +70,26 @@ public static ClassLoader latestUserDefinedLoader0() {
7270 private static long directMemory ;
7371 @ Alias @ InjectAccessors (PageAlignDirectMemoryAccessors .class ) //
7472 private static boolean pageAlignDirectMemory ;
75-
76- @ Alias //
77- public static native void initLevel (int newVal );
78-
79- @ Alias //
80- public static native int initLevel ();
8173}
8274
8375final class DirectMemoryAccessors {
76+ private static final long DIRECT_MEMORY_DURING_INITIALIZATION = 25 * 1024 * 1024 ;
8477
8578 /*
86- * Full initialization is two-staged. First, we init directMemory to a static value (25MB) so
87- * that initialization of PhysicalMemory has a chance to finish. At that point isInintialized
88- * will be false, since we need to (potentially) set the value to the actual configured heap
89- * size. That can only be done once PhysicalMemory init completed. We'd introduce a cycle
90- * otherwise.
79+ * Not volatile to avoid a memory barrier when reading the values. Instead, an explicit barrier
80+ * is inserted when writing the values.
9181 */
92- private static boolean isInitialized ;
93- private static final int INITIALIZING = 1 ;
94- private static final int INITIALIZED = 2 ;
95- private static final AtomicInteger INIT_COUNT = new AtomicInteger ();
96- private static final long STATIC_DIRECT_MEMORY_AMOUNT = 25 * 1024 * 1024 ;
82+ private static boolean initialized ;
9783 private static long directMemory ;
9884
9985 static long getDirectMemory () {
100- if (!isInitialized ) {
101- initialize ();
86+ if (!initialized ) {
87+ return tryInitialize ();
10288 }
10389 return directMemory ;
10490 }
10591
106- private static void initialize () {
107- if (INIT_COUNT .get () == INITIALIZED ) {
108- /*
109- * Safeguard for recursive init
110- */
111- return ;
112- }
92+ private static long tryInitialize () {
11393 /*
11494 * The JDK method VM.saveAndRemoveProperties looks at the system property
11595 * "sun.nio.MaxDirectMemorySize". However, that property is always set by the Java HotSpot
@@ -120,72 +100,41 @@ private static void initialize() {
120100 if (newDirectMemory == 0 ) {
121101 /*
122102 * No value explicitly specified. The default in the JDK in this case is the maximum
123- * heap size. However, we cannot rely on Runtime.maxMemory() until PhysicalMemory has
124- * fully initialized. Runtime.maxMemory() has a dependency on PhysicalMemory.size()
125- * which in turn depends on container support which might use NIO. To avoid this cycle,
126- * we first initialize the 'directMemory' field to an arbitrary value (25MB), and only
127- * use the Runtime.maxMemory() API once PhysicalMemory has fully initialized.
103+ * heap size.
128104 */
129- if (! PhysicalMemory .isInitialized ()) {
105+ if (PhysicalMemory .isInitializationInProgress ()) {
130106 /*
131- * While initializing physical memory we might end up back here with an INIT_COUNT
132- * of 1, since we read the directMemory field during container support code
133- * execution which runs when PhysicalMemory is still initializing.
107+ * When initializing PhysicalMemory, we use NIO/cgroups code that calls
108+ * VM.getDirectMemory(). When this initialization is in progress, we need to prevent
109+ * that Runtime.maxMemory() is called below because it would trigger a recursive
110+ * initialization of PhysicalMemory. So, we return a temporary value.
134111 */
135- VMError .guarantee (INIT_COUNT .get () <= INITIALIZING , "Initial run needs to have init count 0 or 1" );
136- newDirectMemory = STATIC_DIRECT_MEMORY_AMOUNT ; // Static value during initialization
137- INIT_COUNT .setRelease (INITIALIZING );
138- } else {
139- VMError .guarantee (INIT_COUNT .get () <= INITIALIZING , "Runtime.maxMemory() invariant" );
140- /*
141- * Once we know PhysicalMemory has been properly initialized we can use
142- * Runtime.maxMemory(). Note that we might end up in this branch for code explicitly
143- * using the JDK cgroups code. At that point PhysicalMemory has likely been
144- * initialized.
145- */
146- INIT_COUNT .setRelease (INITIALIZED );
147- newDirectMemory = Runtime .getRuntime ().maxMemory ();
148- }
149- } else {
150- /*
151- * For explicitly set direct memory we are done
152- */
153- Unsafe .getUnsafe ().storeFence ();
154- directMemory = newDirectMemory ;
155- isInitialized = true ;
156- if (Target_jdk_internal_misc_VM .initLevel () < 1 ) {
157- // only the first accessor needs to set this
158- Target_jdk_internal_misc_VM .initLevel (1 );
112+ return DIRECT_MEMORY_DURING_INITIALIZATION ;
159113 }
160- return ;
114+ newDirectMemory = Runtime . getRuntime (). maxMemory () ;
161115 }
162- VMError .guarantee (newDirectMemory > 0 , "New direct memory should be initialized" );
163116
164- Unsafe .getUnsafe ().storeFence ();
117+ /*
118+ * The initialization is not synchronized, so multiple threads can race. Usually this will
119+ * lead to the same value, unless the runtime options are modified concurrently - which is
120+ * possible but not a case we care about.
121+ */
165122 directMemory = newDirectMemory ;
166- if (PhysicalMemory .isInitialized () && INITIALIZED == INIT_COUNT .get ()) {
167- /*
168- * Complete initialization hand-shake once PhysicalMemory is properly initialized. Also
169- * set the VM init level to 1 so as to provoke the NIO code to re-set the internal
170- * MAX_MEMORY field.
171- */
172- isInitialized = true ;
173- if (Target_jdk_internal_misc_VM .initLevel () < 1 ) {
174- // only the first accessor needs to set this
175- Target_jdk_internal_misc_VM .initLevel (1 );
176- }
177- }
123+
124+ /* Ensure values are published to other threads before marking fields as initialized. */
125+ Unsafe .getUnsafe ().storeFence ();
126+ initialized = true ;
127+
128+ return newDirectMemory ;
178129 }
179130}
180131
181132final class PageAlignDirectMemoryAccessors {
182-
183133 /*
184134 * Not volatile to avoid a memory barrier when reading the values. Instead, an explicit barrier
185135 * is inserted when writing the values.
186136 */
187137 private static boolean initialized ;
188-
189138 private static boolean pageAlignDirectMemory ;
190139
191140 static boolean getPageAlignDirectMemory () {
0 commit comments