24
24
*/
25
25
package com .oracle .svm .core .genscavenge ;
26
26
27
- import java .util .concurrent .atomic .AtomicBoolean ;
28
-
29
- import jdk .graal .compiler .word .Word ;
30
27
import org .graalvm .nativeimage .Platform ;
31
28
import org .graalvm .nativeimage .Platforms ;
32
29
import org .graalvm .word .UnsignedWord ;
36
33
import com .oracle .svm .core .heap .PhysicalMemory ;
37
34
import com .oracle .svm .core .heap .ReferenceAccess ;
38
35
import com .oracle .svm .core .jdk .UninterruptibleUtils ;
36
+ import com .oracle .svm .core .os .CommittedMemoryProvider ;
37
+ import com .oracle .svm .core .thread .JavaSpinLockUtils ;
39
38
import com .oracle .svm .core .thread .VMOperation ;
40
39
import com .oracle .svm .core .util .UnsignedUtils ;
41
40
import com .oracle .svm .core .util .VMError ;
42
41
43
42
import jdk .graal .compiler .api .replacements .Fold ;
44
- import jdk .graal .compiler .nodes .PauseNode ;
43
+ import jdk .graal .compiler .word .Word ;
44
+ import jdk .internal .misc .Unsafe ;
45
45
46
+ /**
47
+ * Note that a lot of methods in this class are final. Subclasses may only override certain methods
48
+ * to avoid inconsistencies between the different heap size values.
49
+ */
46
50
abstract class AbstractCollectionPolicy implements CollectionPolicy {
47
51
48
52
protected static final int MIN_SPACE_SIZE_IN_ALIGNED_CHUNKS = 8 ;
@@ -54,6 +58,9 @@ static int getMaxSurvivorSpaces(Integer userValue) {
54
58
return (userValue != null ) ? userValue : AbstractCollectionPolicy .MAX_TENURING_THRESHOLD ;
55
59
}
56
60
61
+ private static final Unsafe U = Unsafe .getUnsafe ();
62
+ private static final long SIZES_UPDATE_LOCK_OFFSET = U .objectFieldOffset (AbstractCollectionPolicy .class , "sizesUpdateLock" );
63
+
57
64
/*
58
65
* Constants that can be made options if desirable. These are -XX options in HotSpot, refer to
59
66
* their descriptions for details. The values are HotSpot defaults unless labeled otherwise.
@@ -78,8 +85,8 @@ static int getMaxSurvivorSpaces(Integer userValue) {
78
85
protected UnsignedWord oldSize ;
79
86
protected int tenuringThreshold ;
80
87
81
- protected volatile SizeParameters sizes ;
82
- private final AtomicBoolean sizesUpdateSpinLock = new AtomicBoolean () ;
88
+ protected volatile SizeParameters sizes = null ;
89
+ @ SuppressWarnings ( "unused" ) private volatile int sizesUpdateLock ;
83
90
84
91
protected AbstractCollectionPolicy (int initialNewRatio , int initialTenuringThreshold ) {
85
92
this .initialNewRatio = initialNewRatio ;
@@ -145,40 +152,49 @@ protected void guaranteeSizeParametersInitialized() {
145
152
146
153
@ Override
147
154
public void updateSizeParameters () {
148
- SizeParameters params = computeSizeParameters (sizes );
149
- SizeParameters previous = sizes ;
150
- if (previous != null && params .equal (previous )) {
155
+ /*
156
+ * Read the old object before computing the new values. Otherwise, we risk reusing an
157
+ * outdated SizeParameters object.
158
+ */
159
+ SizeParameters prevParams = sizes ;
160
+ SizeParameters newParams = computeSizeParameters (prevParams );
161
+ if (prevParams != null && newParams .equal (prevParams )) {
151
162
return ; // nothing to do
152
163
}
153
- while (!sizesUpdateSpinLock .compareAndSet (false , true )) {
154
- /*
155
- * We use a primitive spin lock because at this point, the current thread might be
156
- * unable to use a Java lock (e.g. no Thread object yet), and the critical section is
157
- * short, so we do not want to suspend and wake up threads for it.
158
- */
159
- PauseNode .pause ();
160
- }
164
+ updateSizeParameters0 (newParams , prevParams );
165
+ guaranteeSizeParametersInitialized (); // sanity
166
+ }
167
+
168
+ @ Uninterruptible (reason = "Holding the spin lock at a safepoint can result in deadlocks." )
169
+ private void updateSizeParameters0 (SizeParameters newParams , SizeParameters prevParams ) {
170
+ /*
171
+ * We use a primitive spin lock because at this point, the current thread might be unable to
172
+ * use a Java lock (e.g. no Thread object yet), and the critical section is short, so we do
173
+ * not want to suspend and wake up threads for it.
174
+ */
175
+ JavaSpinLockUtils .lockNoTransition (this , SIZES_UPDATE_LOCK_OFFSET );
161
176
try {
162
- updateSizeParametersLocked (params , previous );
177
+ if (sizes != prevParams ) {
178
+ /*
179
+ * Some other thread beat us and we cannot tell if our values or their values are
180
+ * newer, so back off - any newer values will be applied eventually.
181
+ */
182
+ return ;
183
+ }
184
+ updateSizeParametersLocked (newParams , prevParams );
163
185
} finally {
164
- sizesUpdateSpinLock . set ( false );
186
+ JavaSpinLockUtils . unlock ( this , SIZES_UPDATE_LOCK_OFFSET );
165
187
}
166
- guaranteeSizeParametersInitialized (); // sanity
167
188
}
168
189
169
- @ Uninterruptible (reason = "Must be atomic with regard to garbage collection." )
170
- private void updateSizeParametersLocked (SizeParameters params , SizeParameters previous ) {
171
- if (sizes != previous ) {
172
- // Some other thread beat us and we cannot tell if our values or their values are newer,
173
- // so back off -- any newer values will be applied eventually.
174
- return ;
175
- }
176
- sizes = params ;
190
+ @ Uninterruptible (reason = "Holding the spin lock at a safepoint can result in deadlocks. Updating the size parameters must be atomic with regard to garbage collection." )
191
+ private void updateSizeParametersLocked (SizeParameters newParams , SizeParameters prevParams ) {
192
+ sizes = newParams ;
177
193
178
- if (previous == null || gcCount () == 0 ) {
179
- survivorSize = params .initialSurvivorSize ;
180
- edenSize = params .initialEdenSize ;
181
- oldSize = params .initialOldSize ();
194
+ if (prevParams == null || gcCount () == 0 ) {
195
+ survivorSize = newParams .initialSurvivorSize ;
196
+ edenSize = newParams .initialEdenSize ;
197
+ oldSize = newParams .initialOldSize ();
182
198
promoSize = UnsignedUtils .min (edenSize , oldSize );
183
199
}
184
200
@@ -191,85 +207,85 @@ private void updateSizeParametersLocked(SizeParameters params, SizeParameters pr
191
207
* We assume that such changes happen very early on and values then adapt reasonably quick,
192
208
* but we must still ensure that computations can handle it (for example, no overflows).
193
209
*/
194
- survivorSize = UnsignedUtils .min (survivorSize , params .maxSurvivorSize ());
210
+ survivorSize = UnsignedUtils .min (survivorSize , newParams .maxSurvivorSize ());
195
211
edenSize = UnsignedUtils .min (edenSize , getMaximumEdenSize ());
196
- oldSize = UnsignedUtils .min (oldSize , params .maxOldSize ());
197
- promoSize = UnsignedUtils .min (promoSize , params .maxOldSize ());
212
+ oldSize = UnsignedUtils .min (oldSize , newParams .maxOldSize ());
213
+ promoSize = UnsignedUtils .min (promoSize , newParams .maxOldSize ());
198
214
}
199
215
200
216
@ Override
201
- public UnsignedWord getInitialEdenSize () {
217
+ public final UnsignedWord getInitialEdenSize () {
202
218
guaranteeSizeParametersInitialized ();
203
219
return sizes .initialEdenSize ;
204
220
}
205
221
206
222
@ Override
207
223
@ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
208
- public UnsignedWord getMaximumEdenSize () {
224
+ public final UnsignedWord getMaximumEdenSize () {
209
225
guaranteeSizeParametersInitialized ();
210
226
return alignDown (sizes .maxYoungSize .subtract (survivorSize .multiply (2 )));
211
227
}
212
228
213
229
@ Override
214
- public UnsignedWord getMaximumHeapSize () {
230
+ public final UnsignedWord getMaximumHeapSize () {
215
231
guaranteeSizeParametersInitialized ();
216
232
return sizes .maxHeapSize ;
217
233
}
218
234
219
235
@ Override
220
- public UnsignedWord getMaximumYoungGenerationSize () {
236
+ public final UnsignedWord getMaximumYoungGenerationSize () {
221
237
guaranteeSizeParametersInitialized ();
222
238
return sizes .maxYoungSize ;
223
239
}
224
240
225
241
@ Override
226
- public UnsignedWord getInitialSurvivorSize () {
242
+ public final UnsignedWord getInitialSurvivorSize () {
227
243
guaranteeSizeParametersInitialized ();
228
244
return sizes .initialSurvivorSize ;
229
245
}
230
246
231
247
@ Override
232
- public UnsignedWord getMaximumSurvivorSize () {
248
+ public final UnsignedWord getMaximumSurvivorSize () {
233
249
guaranteeSizeParametersInitialized ();
234
250
return sizes .maxSurvivorSize ();
235
251
}
236
252
237
253
@ Override
238
- public UnsignedWord getCurrentHeapCapacity () {
254
+ public final UnsignedWord getCurrentHeapCapacity () {
239
255
assert VMOperation .isGCInProgress () : "use only during GC" ;
240
256
guaranteeSizeParametersInitialized ();
241
257
return edenSize .add (survivorSize ).add (oldSize );
242
258
}
243
259
244
260
@ Override
245
261
@ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
246
- public UnsignedWord getSurvivorSpacesCapacity () {
262
+ public final UnsignedWord getSurvivorSpacesCapacity () {
247
263
assert VMOperation .isGCInProgress () : "use only during GC" ;
248
264
guaranteeSizeParametersInitialized ();
249
265
return survivorSize ;
250
266
}
251
267
252
268
@ Override
253
269
@ Uninterruptible (reason = "Ensure reading a consistent value." )
254
- public UnsignedWord getYoungGenerationCapacity () {
270
+ public final UnsignedWord getYoungGenerationCapacity () {
255
271
guaranteeSizeParametersInitialized ();
256
272
return edenSize .add (survivorSize );
257
273
}
258
274
259
275
@ Override
260
- public UnsignedWord getInitialOldSize () {
276
+ public final UnsignedWord getInitialOldSize () {
261
277
guaranteeSizeParametersInitialized ();
262
278
return sizes .initialOldSize ();
263
279
}
264
280
265
281
@ Override
266
- public UnsignedWord getMaximumOldSize () {
282
+ public final UnsignedWord getMaximumOldSize () {
267
283
guaranteeSizeParametersInitialized ();
268
284
return sizes .maxOldSize ();
269
285
}
270
286
271
287
@ Override
272
- public UnsignedWord getOldGenerationCapacity () {
288
+ public final UnsignedWord getOldGenerationCapacity () {
273
289
guaranteeSizeParametersInitialized ();
274
290
return oldSize ;
275
291
}
@@ -308,15 +324,14 @@ public void onCollectionBegin(boolean completeCollection, long requestingNanoTim
308
324
}
309
325
310
326
@ Override
311
- public UnsignedWord getMinimumHeapSize () {
327
+ public final UnsignedWord getMinimumHeapSize () {
312
328
return sizes .minHeapSize ;
313
329
}
314
330
315
331
@ Uninterruptible (reason = "Called from uninterruptible code." , mayBeInlined = true )
316
332
protected abstract long gcCount ();
317
333
318
334
protected SizeParameters computeSizeParameters (SizeParameters existing ) {
319
- UnsignedWord addressSpaceSize = ReferenceAccess .singleton ().getAddressSpaceSize ();
320
335
UnsignedWord minYoungSpaces = minSpaceSize (); // eden
321
336
if (HeapParameters .getMaxSurvivorSpaces () > 0 ) {
322
337
minYoungSpaces = minYoungSpaces .add (minSpaceSize ().multiply (2 )); // survivor from and to
@@ -331,7 +346,7 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) {
331
346
maxHeap = PhysicalMemory .size ().unsignedDivide (100 ).multiply (HeapParameters .getMaximumHeapSizePercent ());
332
347
}
333
348
UnsignedWord unadjustedMaxHeap = maxHeap ;
334
- maxHeap = UnsignedUtils .clamp (alignDown (maxHeap ), minAllSpaces , alignDown (addressSpaceSize ));
349
+ maxHeap = UnsignedUtils .clamp (alignDown (maxHeap ), minAllSpaces , alignDown (getHeapSizeLimit () ));
335
350
336
351
UnsignedWord maxYoung ;
337
352
long optionMaxYoung = SubstrateGCOptions .MaxNewSize .getValue ();
@@ -342,11 +357,11 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) {
342
357
} else {
343
358
maxYoung = maxHeap .unsignedDivide (AbstractCollectionPolicy .NEW_RATIO + 1 );
344
359
}
345
- maxYoung = UnsignedUtils .clamp (alignDown (maxYoung ), minYoungSpaces , maxHeap . subtract ( minSpaceSize () ));
360
+ maxYoung = UnsignedUtils .clamp (alignDown (maxYoung ), minYoungSpaces , getYoungSizeLimit ( maxHeap ));
346
361
347
362
UnsignedWord maxOld = alignDown (maxHeap .subtract (maxYoung ));
348
363
maxHeap = maxYoung .add (maxOld );
349
- VMError .guarantee (maxOld .aboveOrEqual (minSpaceSize ()) && maxHeap .belowOrEqual (addressSpaceSize ) &&
364
+ VMError .guarantee (maxOld .aboveOrEqual (minSpaceSize ()) && maxHeap .belowOrEqual (getHeapSizeLimit () ) &&
350
365
(maxHeap .belowOrEqual (unadjustedMaxHeap ) || unadjustedMaxHeap .belowThan (minAllSpaces )));
351
366
352
367
UnsignedWord minHeap = Word .zero ();
@@ -384,6 +399,14 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) {
384
399
return SizeParameters .get (existing , maxHeap , maxYoung , initialHeap , initialEden , initialSurvivor , minHeap );
385
400
}
386
401
402
+ protected UnsignedWord getHeapSizeLimit () {
403
+ return CommittedMemoryProvider .get ().getCollectedHeapAddressSpaceSize ();
404
+ }
405
+
406
+ protected UnsignedWord getYoungSizeLimit (UnsignedWord maxHeap ) {
407
+ return maxHeap .subtract (minSpaceSize ());
408
+ }
409
+
387
410
protected UnsignedWord getInitialHeapSize () {
388
411
return AbstractCollectionPolicy .INITIAL_HEAP_SIZE ;
389
412
}
@@ -419,7 +442,7 @@ private SizeParameters(UnsignedWord maxHeapSize, UnsignedWord maxYoungSize, Unsi
419
442
assert initialHeapSize .belowOrEqual (maxHeapSize );
420
443
assert maxSurvivorSize ().belowThan (maxYoungSize );
421
444
assert maxYoungSize .add (maxOldSize ()).equal (maxHeapSize );
422
- assert maxHeapSize .belowOrEqual (ReferenceAccess .singleton ().getAddressSpaceSize ());
445
+ assert maxHeapSize .belowOrEqual (ReferenceAccess .singleton ().getMaxAddressSpaceSize ());
423
446
assert initialEdenSize .add (initialSurvivorSize .multiply (2 )).equal (initialYoungSize ());
424
447
assert initialYoungSize ().add (initialOldSize ()).equal (initialHeapSize );
425
448
}
0 commit comments