Skip to content

Commit f27bf50

Browse files
[GR-61347] [GR-62499] [GR-62610] [GR-62658] Various serial GC fixes and added option -XX:+PrintVMInfoAndExit.
PullRequest: graal/20207
2 parents 5fe9dbc + 4d85c30 commit f27bf50

18 files changed

+182
-102
lines changed

substratevm/mx.substratevm/suite.py

+1
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@
400400
],
401401
"requiresConcealed" : {
402402
"java.base": [
403+
"jdk.internal.misc",
403404
"sun.nio.ch",
404405
],
405406
"java.management": [

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AbstractCollectionPolicy.java

+76-53
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,6 @@
2424
*/
2525
package com.oracle.svm.core.genscavenge;
2626

27-
import java.util.concurrent.atomic.AtomicBoolean;
28-
29-
import jdk.graal.compiler.word.Word;
3027
import org.graalvm.nativeimage.Platform;
3128
import org.graalvm.nativeimage.Platforms;
3229
import org.graalvm.word.UnsignedWord;
@@ -36,13 +33,20 @@
3633
import com.oracle.svm.core.heap.PhysicalMemory;
3734
import com.oracle.svm.core.heap.ReferenceAccess;
3835
import com.oracle.svm.core.jdk.UninterruptibleUtils;
36+
import com.oracle.svm.core.os.CommittedMemoryProvider;
37+
import com.oracle.svm.core.thread.JavaSpinLockUtils;
3938
import com.oracle.svm.core.thread.VMOperation;
4039
import com.oracle.svm.core.util.UnsignedUtils;
4140
import com.oracle.svm.core.util.VMError;
4241

4342
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;
4545

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+
*/
4650
abstract class AbstractCollectionPolicy implements CollectionPolicy {
4751

4852
protected static final int MIN_SPACE_SIZE_IN_ALIGNED_CHUNKS = 8;
@@ -54,6 +58,9 @@ static int getMaxSurvivorSpaces(Integer userValue) {
5458
return (userValue != null) ? userValue : AbstractCollectionPolicy.MAX_TENURING_THRESHOLD;
5559
}
5660

61+
private static final Unsafe U = Unsafe.getUnsafe();
62+
private static final long SIZES_UPDATE_LOCK_OFFSET = U.objectFieldOffset(AbstractCollectionPolicy.class, "sizesUpdateLock");
63+
5764
/*
5865
* Constants that can be made options if desirable. These are -XX options in HotSpot, refer to
5966
* their descriptions for details. The values are HotSpot defaults unless labeled otherwise.
@@ -78,8 +85,8 @@ static int getMaxSurvivorSpaces(Integer userValue) {
7885
protected UnsignedWord oldSize;
7986
protected int tenuringThreshold;
8087

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;
8390

8491
protected AbstractCollectionPolicy(int initialNewRatio, int initialTenuringThreshold) {
8592
this.initialNewRatio = initialNewRatio;
@@ -145,40 +152,49 @@ protected void guaranteeSizeParametersInitialized() {
145152

146153
@Override
147154
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)) {
151162
return; // nothing to do
152163
}
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);
161176
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);
163185
} finally {
164-
sizesUpdateSpinLock.set(false);
186+
JavaSpinLockUtils.unlock(this, SIZES_UPDATE_LOCK_OFFSET);
165187
}
166-
guaranteeSizeParametersInitialized(); // sanity
167188
}
168189

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;
177193

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();
182198
promoSize = UnsignedUtils.min(edenSize, oldSize);
183199
}
184200

@@ -191,85 +207,85 @@ private void updateSizeParametersLocked(SizeParameters params, SizeParameters pr
191207
* We assume that such changes happen very early on and values then adapt reasonably quick,
192208
* but we must still ensure that computations can handle it (for example, no overflows).
193209
*/
194-
survivorSize = UnsignedUtils.min(survivorSize, params.maxSurvivorSize());
210+
survivorSize = UnsignedUtils.min(survivorSize, newParams.maxSurvivorSize());
195211
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());
198214
}
199215

200216
@Override
201-
public UnsignedWord getInitialEdenSize() {
217+
public final UnsignedWord getInitialEdenSize() {
202218
guaranteeSizeParametersInitialized();
203219
return sizes.initialEdenSize;
204220
}
205221

206222
@Override
207223
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
208-
public UnsignedWord getMaximumEdenSize() {
224+
public final UnsignedWord getMaximumEdenSize() {
209225
guaranteeSizeParametersInitialized();
210226
return alignDown(sizes.maxYoungSize.subtract(survivorSize.multiply(2)));
211227
}
212228

213229
@Override
214-
public UnsignedWord getMaximumHeapSize() {
230+
public final UnsignedWord getMaximumHeapSize() {
215231
guaranteeSizeParametersInitialized();
216232
return sizes.maxHeapSize;
217233
}
218234

219235
@Override
220-
public UnsignedWord getMaximumYoungGenerationSize() {
236+
public final UnsignedWord getMaximumYoungGenerationSize() {
221237
guaranteeSizeParametersInitialized();
222238
return sizes.maxYoungSize;
223239
}
224240

225241
@Override
226-
public UnsignedWord getInitialSurvivorSize() {
242+
public final UnsignedWord getInitialSurvivorSize() {
227243
guaranteeSizeParametersInitialized();
228244
return sizes.initialSurvivorSize;
229245
}
230246

231247
@Override
232-
public UnsignedWord getMaximumSurvivorSize() {
248+
public final UnsignedWord getMaximumSurvivorSize() {
233249
guaranteeSizeParametersInitialized();
234250
return sizes.maxSurvivorSize();
235251
}
236252

237253
@Override
238-
public UnsignedWord getCurrentHeapCapacity() {
254+
public final UnsignedWord getCurrentHeapCapacity() {
239255
assert VMOperation.isGCInProgress() : "use only during GC";
240256
guaranteeSizeParametersInitialized();
241257
return edenSize.add(survivorSize).add(oldSize);
242258
}
243259

244260
@Override
245261
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
246-
public UnsignedWord getSurvivorSpacesCapacity() {
262+
public final UnsignedWord getSurvivorSpacesCapacity() {
247263
assert VMOperation.isGCInProgress() : "use only during GC";
248264
guaranteeSizeParametersInitialized();
249265
return survivorSize;
250266
}
251267

252268
@Override
253269
@Uninterruptible(reason = "Ensure reading a consistent value.")
254-
public UnsignedWord getYoungGenerationCapacity() {
270+
public final UnsignedWord getYoungGenerationCapacity() {
255271
guaranteeSizeParametersInitialized();
256272
return edenSize.add(survivorSize);
257273
}
258274

259275
@Override
260-
public UnsignedWord getInitialOldSize() {
276+
public final UnsignedWord getInitialOldSize() {
261277
guaranteeSizeParametersInitialized();
262278
return sizes.initialOldSize();
263279
}
264280

265281
@Override
266-
public UnsignedWord getMaximumOldSize() {
282+
public final UnsignedWord getMaximumOldSize() {
267283
guaranteeSizeParametersInitialized();
268284
return sizes.maxOldSize();
269285
}
270286

271287
@Override
272-
public UnsignedWord getOldGenerationCapacity() {
288+
public final UnsignedWord getOldGenerationCapacity() {
273289
guaranteeSizeParametersInitialized();
274290
return oldSize;
275291
}
@@ -308,15 +324,14 @@ public void onCollectionBegin(boolean completeCollection, long requestingNanoTim
308324
}
309325

310326
@Override
311-
public UnsignedWord getMinimumHeapSize() {
327+
public final UnsignedWord getMinimumHeapSize() {
312328
return sizes.minHeapSize;
313329
}
314330

315331
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
316332
protected abstract long gcCount();
317333

318334
protected SizeParameters computeSizeParameters(SizeParameters existing) {
319-
UnsignedWord addressSpaceSize = ReferenceAccess.singleton().getAddressSpaceSize();
320335
UnsignedWord minYoungSpaces = minSpaceSize(); // eden
321336
if (HeapParameters.getMaxSurvivorSpaces() > 0) {
322337
minYoungSpaces = minYoungSpaces.add(minSpaceSize().multiply(2)); // survivor from and to
@@ -331,7 +346,7 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) {
331346
maxHeap = PhysicalMemory.size().unsignedDivide(100).multiply(HeapParameters.getMaximumHeapSizePercent());
332347
}
333348
UnsignedWord unadjustedMaxHeap = maxHeap;
334-
maxHeap = UnsignedUtils.clamp(alignDown(maxHeap), minAllSpaces, alignDown(addressSpaceSize));
349+
maxHeap = UnsignedUtils.clamp(alignDown(maxHeap), minAllSpaces, alignDown(getHeapSizeLimit()));
335350

336351
UnsignedWord maxYoung;
337352
long optionMaxYoung = SubstrateGCOptions.MaxNewSize.getValue();
@@ -342,11 +357,11 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) {
342357
} else {
343358
maxYoung = maxHeap.unsignedDivide(AbstractCollectionPolicy.NEW_RATIO + 1);
344359
}
345-
maxYoung = UnsignedUtils.clamp(alignDown(maxYoung), minYoungSpaces, maxHeap.subtract(minSpaceSize()));
360+
maxYoung = UnsignedUtils.clamp(alignDown(maxYoung), minYoungSpaces, getYoungSizeLimit(maxHeap));
346361

347362
UnsignedWord maxOld = alignDown(maxHeap.subtract(maxYoung));
348363
maxHeap = maxYoung.add(maxOld);
349-
VMError.guarantee(maxOld.aboveOrEqual(minSpaceSize()) && maxHeap.belowOrEqual(addressSpaceSize) &&
364+
VMError.guarantee(maxOld.aboveOrEqual(minSpaceSize()) && maxHeap.belowOrEqual(getHeapSizeLimit()) &&
350365
(maxHeap.belowOrEqual(unadjustedMaxHeap) || unadjustedMaxHeap.belowThan(minAllSpaces)));
351366

352367
UnsignedWord minHeap = Word.zero();
@@ -384,6 +399,14 @@ protected SizeParameters computeSizeParameters(SizeParameters existing) {
384399
return SizeParameters.get(existing, maxHeap, maxYoung, initialHeap, initialEden, initialSurvivor, minHeap);
385400
}
386401

402+
protected UnsignedWord getHeapSizeLimit() {
403+
return CommittedMemoryProvider.get().getCollectedHeapAddressSpaceSize();
404+
}
405+
406+
protected UnsignedWord getYoungSizeLimit(UnsignedWord maxHeap) {
407+
return maxHeap.subtract(minSpaceSize());
408+
}
409+
387410
protected UnsignedWord getInitialHeapSize() {
388411
return AbstractCollectionPolicy.INITIAL_HEAP_SIZE;
389412
}
@@ -419,7 +442,7 @@ private SizeParameters(UnsignedWord maxHeapSize, UnsignedWord maxYoungSize, Unsi
419442
assert initialHeapSize.belowOrEqual(maxHeapSize);
420443
assert maxSurvivorSize().belowThan(maxYoungSize);
421444
assert maxYoungSize.add(maxOldSize()).equal(maxHeapSize);
422-
assert maxHeapSize.belowOrEqual(ReferenceAccess.singleton().getAddressSpaceSize());
445+
assert maxHeapSize.belowOrEqual(ReferenceAccess.singleton().getMaxAddressSpaceSize());
423446
assert initialEdenSize.add(initialSurvivorSize.multiply(2)).equal(initialYoungSize());
424447
assert initialYoungSize().add(initialOldSize()).equal(initialHeapSize);
425448
}

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/AddressRangeCommittedMemoryProvider.java

+8-11
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,9 @@
2828
import static com.oracle.svm.core.util.PointerUtils.roundDown;
2929
import static com.oracle.svm.core.util.PointerUtils.roundUp;
3030
import static com.oracle.svm.core.util.VMError.guarantee;
31-
import static jdk.graal.compiler.word.Word.unsigned;
3231
import static jdk.graal.compiler.word.Word.nullPointer;
32+
import static jdk.graal.compiler.word.Word.unsigned;
3333

34-
import jdk.graal.compiler.word.Word;
3534
import org.graalvm.nativeimage.Isolate;
3635
import org.graalvm.nativeimage.Platform;
3736
import org.graalvm.nativeimage.Platforms;
@@ -74,6 +73,7 @@
7473
import com.oracle.svm.core.util.VMError;
7574

7675
import jdk.graal.compiler.api.replacements.Fold;
76+
import jdk.graal.compiler.word.Word;
7777

7878
/**
7979
* Reserves a fixed-size address range and provides memory from it by committing and uncommitting
@@ -144,16 +144,12 @@ public AddressRangeCommittedMemoryProvider() {
144144
@Override
145145
@Uninterruptible(reason = "Still being initialized.")
146146
public int initialize(WordPointer heapBasePointer, IsolateArguments arguments) {
147-
UnsignedWord addressSpaceSize = ReferenceAccess.singleton().getAddressSpaceSize();
147+
UnsignedWord maxAddressSpaceSize = ReferenceAccess.singleton().getMaxAddressSpaceSize();
148148
UnsignedWord reserved = Word.unsigned(IsolateArgumentAccess.readLong(arguments, IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.ReservedAddressSpaceSize)));
149149
if (reserved.equal(0)) {
150-
/*
151-
* By default, always reserve at least 32 GB of address space. If a large maximum heap
152-
* size was specified, then reserve 2x of that maximum heap size (assuming that the max.
153-
* address space size is large enough for that).
154-
*/
155-
UnsignedWord maxHeapSize = Word.unsigned(IsolateArgumentAccess.readLong(arguments, IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.MaxHeapSize))).multiply(2);
156-
reserved = UnsignedUtils.clamp(maxHeapSize, Word.unsigned(MIN_RESERVED_ADDRESS_SPACE_SIZE), addressSpaceSize);
150+
/* Reserve a 32 GB address space, except if a larger heap size was specified. */
151+
UnsignedWord maxHeapSize = Word.unsigned(IsolateArgumentAccess.readLong(arguments, IsolateArgumentParser.getOptionIndex(SubstrateGCOptions.MaxHeapSize)));
152+
reserved = UnsignedUtils.clamp(maxHeapSize, Word.unsigned(MIN_RESERVED_ADDRESS_SPACE_SIZE), maxAddressSpaceSize);
157153
}
158154

159155
UnsignedWord alignment = unsigned(Heap.getHeap().getPreferredAddressSpaceAlignment());
@@ -790,7 +786,8 @@ private boolean isInAllocList(FreeListNode node) {
790786
return false;
791787
}
792788

793-
public UnsignedWord getReservedSpaceSize() {
789+
@Override
790+
public UnsignedWord getReservedAddressSpaceSize() {
794791
return reservedSpaceSize;
795792
}
796793

substratevm/src/com.oracle.svm.core.genscavenge/src/com/oracle/svm/core/genscavenge/BasicCollectionPolicies.java

+5-10
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626

2727
import static com.oracle.svm.core.genscavenge.CollectionPolicy.shouldCollectYoungGenSeparately;
2828

29-
import jdk.graal.compiler.word.Word;
3029
import org.graalvm.nativeimage.Platform;
3130
import org.graalvm.nativeimage.Platforms;
3231
import org.graalvm.word.UnsignedWord;
@@ -35,11 +34,13 @@
3534
import com.oracle.svm.core.Uninterruptible;
3635
import com.oracle.svm.core.heap.GCCause;
3736
import com.oracle.svm.core.heap.PhysicalMemory;
38-
import com.oracle.svm.core.heap.ReferenceAccess;
37+
import com.oracle.svm.core.os.CommittedMemoryProvider;
3938
import com.oracle.svm.core.util.TimeUtils;
4039
import com.oracle.svm.core.util.UnsignedUtils;
4140
import com.oracle.svm.core.util.VMError;
4241

42+
import jdk.graal.compiler.word.Word;
43+
4344
/** Basic/legacy garbage collection policies. */
4445
final class BasicCollectionPolicies {
4546
@Platforms(Platform.HOSTED_ONLY.class)
@@ -101,15 +102,9 @@ public final UnsignedWord getMaximumHeapSize() {
101102
return Word.unsigned(runtimeValue);
102103
}
103104

104-
/*
105-
* If the physical size is known yet, the maximum size of the heap is a fraction of the
106-
* size of the physical memory.
107-
*/
108-
UnsignedWord addressSpaceSize = ReferenceAccess.singleton().getAddressSpaceSize();
109-
UnsignedWord physicalMemorySize = PhysicalMemory.size();
105+
UnsignedWord addressSpaceSize = CommittedMemoryProvider.get().getCollectedHeapAddressSpaceSize();
110106
int maximumHeapSizePercent = HeapParameters.getMaximumHeapSizePercent();
111-
/* Do not cache because `-Xmx` option parsing may not have happened yet. */
112-
UnsignedWord result = physicalMemorySize.unsignedDivide(100).multiply(maximumHeapSizePercent);
107+
UnsignedWord result = PhysicalMemory.size().unsignedDivide(100).multiply(maximumHeapSizePercent);
113108
return UnsignedUtils.min(result, addressSpaceSize);
114109
}
115110

0 commit comments

Comments
 (0)