Skip to content

Commit bcd6019

Browse files
[GR-71654] [GR-72101] [GR-72102] Remove the Java heap allocation in AbstractCollectionPolicy.updateSizeParameters() + fixes and optimizations.
PullRequest: graal/22769
2 parents ca4632d + 333471d commit bcd6019

File tree

20 files changed

+840
-333
lines changed

20 files changed

+840
-333
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/options/ModifiableOptionValues.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ public void update(UnmodifiableEconomicMap<OptionKey<?>, Object> values) {
106106
}
107107
}
108108
} while (!v.compareAndSet(expect, newMap));
109+
110+
UnmodifiableMapCursor<OptionKey<?>, Object> cursor = values.getEntries();
111+
while (cursor.advance()) {
112+
OptionKey<?> key = cursor.getKey();
113+
key.afterValueUpdate();
114+
}
109115
}
110116

111117
@Override

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

Lines changed: 119 additions & 189 deletions
Large diffs are not rendered by default.

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

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
import org.graalvm.word.UnsignedWord;
3030

3131
import com.oracle.svm.core.Isolates;
32-
import com.oracle.svm.core.Uninterruptible;
3332
import com.oracle.svm.core.heap.GCCause;
33+
import com.oracle.svm.core.thread.VMOperation;
3434
import com.oracle.svm.core.util.BasedOnJDKFile;
3535
import com.oracle.svm.core.util.TimeUtils;
3636
import com.oracle.svm.core.util.Timer;
@@ -108,7 +108,8 @@ class AdaptiveCollectionPolicy extends AbstractCollectionPolicy {
108108
private static final int ADAPTIVE_SIZE_POLICY_INITIALIZING_STEPS = ADAPTIVE_SIZE_POLICY_READY_THRESHOLD;
109109
/**
110110
* The minimum estimated decrease in {@link #gcCost()} in percent to decide in favor of
111-
* expanding a space by 1% of the combined size of {@link #edenSize} and {@link #promoSize}.
111+
* expanding a space by 1% of the combined size of {@link SizeParameters#getEdenSize} and
112+
* {@link SizeParameters#getPromoSize}.
112113
*/
113114
private static final double ADAPTIVE_SIZE_ESTIMATOR_MIN_TOTAL_SIZE_COST_TRADEOFF = 0.5;
114115
/** The effective number of most recent data points used by estimator (exponential decay). */
@@ -212,7 +213,7 @@ public boolean shouldCollectCompletely(boolean followingIncrementalCollection, b
212213
*/
213214
UnsignedWord averagePromoted = UnsignedUtils.fromDouble(avgPromoted.getPaddedAverage());
214215
UnsignedWord promotionEstimate = UnsignedUtils.min(averagePromoted, youngUsed);
215-
UnsignedWord oldFree = oldSize.subtract(oldUsed);
216+
UnsignedWord oldFree = sizes.getOldSize().subtract(oldUsed);
216217
return promotionEstimate.aboveThan(oldFree);
217218
}
218219

@@ -254,7 +255,7 @@ private void computeSurvivorSpaceSizeAndThreshold(boolean isSurvivorOverflow, Un
254255
targetSize = survivorLimit;
255256
decrTenuringThreshold = true;
256257
}
257-
survivorSize = targetSize;
258+
sizes.setSurvivorSize(targetSize);
258259

259260
if (decrTenuringThreshold) {
260261
tenuringThreshold = Math.max(tenuringThreshold - 1, 1);
@@ -264,44 +265,47 @@ private void computeSurvivorSpaceSizeAndThreshold(boolean isSurvivorOverflow, Un
264265
}
265266

266267
protected void computeEdenSpaceSize(@SuppressWarnings("unused") boolean completeCollection, @SuppressWarnings("unused") GCCause cause) {
268+
UnsignedWord curEden = sizes.getEdenSize();
269+
UnsignedWord curPromo = sizes.getPromoSize();
270+
267271
boolean expansionReducesCost = true; // general assumption
268272
if (shouldUseEstimator(youngGenChangeForMinorThroughput, minorGcCost())) {
269-
expansionReducesCost = expansionSignificantlyReducesTotalCost(minorCostEstimator, edenSize, majorGcCost(), promoSize);
273+
expansionReducesCost = expansionSignificantlyReducesTotalCost(minorCostEstimator, curEden, majorGcCost(), curPromo);
270274
/*
271275
* Note that if the estimator thinks expanding does not lead to significant improvement,
272276
* shrink so to not get stuck in a supposed optimum and to keep collecting data points.
273277
*/
274278
}
275279

276-
UnsignedWord desiredEdenSize = edenSize;
280+
UnsignedWord desiredEdenSize = curEden;
277281
if (expansionReducesCost && adjustedMutatorCost() < THROUGHPUT_GOAL && gcCost() > 0) {
278282
// from adjust_eden_for_throughput():
279-
UnsignedWord edenHeapDelta = edenIncrementWithSupplementAlignedUp(edenSize);
283+
UnsignedWord edenHeapDelta = edenIncrementWithSupplementAlignedUp(curEden);
280284
double scaleByRatio = minorGcCost() / gcCost();
281285
assert scaleByRatio >= 0 && scaleByRatio <= 1;
282286
UnsignedWord scaledEdenHeapDelta = UnsignedUtils.fromDouble(scaleByRatio * UnsignedUtils.toDouble(edenHeapDelta));
283287

284288
desiredEdenSize = alignUp(desiredEdenSize.add(scaledEdenHeapDelta));
285-
desiredEdenSize = UnsignedUtils.max(desiredEdenSize, edenSize);
289+
desiredEdenSize = UnsignedUtils.max(desiredEdenSize, curEden);
286290
youngGenChangeForMinorThroughput++;
287291
}
288292
if (!expansionReducesCost || (USE_ADAPTIVE_SIZE_POLICY_FOOTPRINT_GOAL && youngGenPolicyIsReady && adjustedMutatorCost() >= THROUGHPUT_GOAL)) {
289-
UnsignedWord desiredSum = edenSize.add(promoSize);
290-
desiredEdenSize = adjustEdenForFootprint(edenSize, desiredSum);
293+
UnsignedWord desiredSum = curEden.add(curPromo);
294+
desiredEdenSize = adjustEdenForFootprint(curEden, desiredSum);
291295
}
292296
assert isAligned(desiredEdenSize);
293297
desiredEdenSize = minSpaceSize(desiredEdenSize);
294298

295-
UnsignedWord edenLimit = getMaximumEdenSize();
299+
UnsignedWord edenLimit = computeEdenLimit();
296300
if (desiredEdenSize.aboveThan(edenLimit)) {
297301
/*
298302
* If the policy says to get a larger eden but is hitting the limit, don't decrease
299303
* eden. This can lead to a general drifting down of the eden size. Let the tenuring
300304
* calculation push more into the old gen.
301305
*/
302-
desiredEdenSize = UnsignedUtils.max(edenLimit, edenSize);
306+
desiredEdenSize = UnsignedUtils.max(edenLimit, curEden);
303307
}
304-
edenSize = desiredEdenSize;
308+
sizes.setEdenSize(desiredEdenSize);
305309
}
306310

307311
private static boolean shouldUseEstimator(long genChangeForThroughput, double cost) {
@@ -435,13 +439,13 @@ public void onCollectionEnd(boolean completeCollection, GCCause cause) { // {maj
435439

436440
if (completeCollection) {
437441
updateCollectionEndAverages(avgMajorGcCost, avgMajorPause, majorCostEstimator, avgMajorIntervalSeconds,
438-
cause, latestMajorMutatorIntervalNanos, timer.lastIntervalNanos(), promoSize);
442+
cause, latestMajorMutatorIntervalNanos, timer.lastIntervalNanos(), sizes.getPromoSize());
439443
majorCount++;
440444
minorCountSinceMajorCollection = 0;
441445

442446
} else {
443447
updateCollectionEndAverages(avgMinorGcCost, avgMinorPause, minorCostEstimator, null,
444-
cause, latestMinorMutatorIntervalNanos, timer.lastIntervalNanos(), edenSize);
448+
cause, latestMinorMutatorIntervalNanos, timer.lastIntervalNanos(), sizes.getEdenSize());
445449
minorCount++;
446450
minorCountSinceMajorCollection++;
447451

@@ -455,7 +459,7 @@ public void onCollectionEnd(boolean completeCollection, GCCause cause) { // {maj
455459

456460
GCAccounting accounting = GCImpl.getAccounting();
457461
UnsignedWord oldLive = accounting.getOldGenerationAfterChunkBytes();
458-
oldSizeExceededInPreviousCollection = oldLive.aboveThan(oldSize);
462+
oldSizeExceededInPreviousCollection = oldLive.aboveThan(sizes.getOldSize());
459463

460464
if (!completeCollection) {
461465
/*
@@ -471,7 +475,7 @@ public void onCollectionEnd(boolean completeCollection, GCCause cause) { // {maj
471475
UnsignedWord tenuredChunkBytes = accounting.getLastIncrementalCollectionPromotedChunkBytes();
472476
updateAverages(survivorOverflow, survivedChunkBytes, tenuredChunkBytes);
473477

474-
computeSurvivorSpaceSizeAndThreshold(survivorOverflow, sizes.maxSurvivorSize());
478+
computeSurvivorSpaceSizeAndThreshold(survivorOverflow, sizes.getMaxSurvivorSize());
475479
}
476480
if (shouldUpdateStats(cause)) {
477481
computeEdenSpaceSize(completeCollection, cause);
@@ -483,51 +487,55 @@ public void onCollectionEnd(boolean completeCollection, GCCause cause) { // {maj
483487
}
484488

485489
private void computeOldGenSpaceSize(UnsignedWord oldLive) { // compute_old_gen_free_space
490+
UnsignedWord curEden = sizes.getEdenSize();
491+
UnsignedWord curPromo = sizes.getPromoSize();
492+
UnsignedWord curMaxOld = sizes.getMaxOldSize();
493+
486494
avgOldLive.sample(oldLive);
487495

488496
// NOTE: if maxOldSize shrunk and difference is negative, unsigned conversion results in 0
489-
UnsignedWord promoLimit = UnsignedUtils.fromDouble(UnsignedUtils.toDouble(sizes.maxOldSize()) - avgOldLive.getAverage());
490-
promoLimit = alignDown(UnsignedUtils.max(promoSize, promoLimit));
497+
UnsignedWord promoLimit = UnsignedUtils.fromDouble(UnsignedUtils.toDouble(curMaxOld) - avgOldLive.getAverage());
498+
promoLimit = alignDown(UnsignedUtils.max(curPromo, promoLimit));
491499

492500
boolean expansionReducesCost = true; // general assumption
493501
if (shouldUseEstimator(oldGenChangeForMajorThroughput, majorGcCost())) {
494-
expansionReducesCost = expansionSignificantlyReducesTotalCost(majorCostEstimator, promoSize, minorGcCost(), edenSize);
502+
expansionReducesCost = expansionSignificantlyReducesTotalCost(majorCostEstimator, curPromo, minorGcCost(), curEden);
495503
/*
496504
* Note that if the estimator thinks expanding does not lead to significant improvement,
497505
* shrink so to not get stuck in a supposed optimum and to keep collecting data points.
498506
*/
499507
}
500508

501-
UnsignedWord desiredPromoSize = promoSize;
509+
UnsignedWord desiredPromoSize = curPromo;
502510
if (expansionReducesCost && adjustedMutatorCost() < THROUGHPUT_GOAL && gcCost() > 0) {
503511
// from adjust_promo_for_throughput():
504-
UnsignedWord promoHeapDelta = promoIncrementWithSupplementAlignedUp(promoSize);
512+
UnsignedWord promoHeapDelta = promoIncrementWithSupplementAlignedUp(curPromo);
505513
double scaleByRatio = majorGcCost() / gcCost();
506514
assert scaleByRatio >= 0 && scaleByRatio <= 1;
507515
UnsignedWord scaledPromoHeapDelta = UnsignedUtils.fromDouble(scaleByRatio * UnsignedUtils.toDouble(promoHeapDelta));
508516

509-
desiredPromoSize = alignUp(promoSize.add(scaledPromoHeapDelta));
510-
desiredPromoSize = UnsignedUtils.max(desiredPromoSize, promoSize);
517+
desiredPromoSize = alignUp(curPromo.add(scaledPromoHeapDelta));
518+
desiredPromoSize = UnsignedUtils.max(desiredPromoSize, curPromo);
511519
oldGenChangeForMajorThroughput++;
512520
}
513521
if (!expansionReducesCost || (USE_ADAPTIVE_SIZE_POLICY_FOOTPRINT_GOAL && youngGenPolicyIsReady && adjustedMutatorCost() >= THROUGHPUT_GOAL)) {
514-
UnsignedWord desiredSum = edenSize.add(promoSize);
515-
desiredPromoSize = adjustPromoForFootprint(promoSize, desiredSum);
522+
UnsignedWord desiredSum = curEden.add(curPromo);
523+
desiredPromoSize = adjustPromoForFootprint(curPromo, desiredSum);
516524
}
517525
assert isAligned(desiredPromoSize);
518526
desiredPromoSize = minSpaceSize(desiredPromoSize);
519527

520528
desiredPromoSize = UnsignedUtils.min(desiredPromoSize, promoLimit);
521-
promoSize = desiredPromoSize;
529+
sizes.setPromoSize(desiredPromoSize);
522530

523531
// from PSOldGen::resize
524532
UnsignedWord desiredFreeSpace = calculatedOldFreeSizeInBytes();
525533
UnsignedWord desiredOldSize = alignUp(oldLive.add(desiredFreeSpace));
526-
oldSize = UnsignedUtils.clamp(desiredOldSize, minSpaceSize(), sizes.maxOldSize());
534+
sizes.setOldSize(UnsignedUtils.clamp(desiredOldSize, minSpaceSize(), curMaxOld));
527535
}
528536

529537
UnsignedWord calculatedOldFreeSizeInBytes() {
530-
return UnsignedUtils.fromDouble(UnsignedUtils.toDouble(promoSize) + avgPromoted.getPaddedAverage());
538+
return UnsignedUtils.fromDouble(UnsignedUtils.toDouble(sizes.getPromoSize()) + avgPromoted.getPaddedAverage());
531539
}
532540

533541
private static UnsignedWord adjustPromoForFootprint(UnsignedWord curPromo, UnsignedWord desiredSum) {
@@ -573,6 +581,11 @@ protected boolean shouldUpdateStats(GCCause cause) { // should_update_{eden,prom
573581
return cause == GenScavengeGCCause.OnAllocation || USE_ADAPTIVE_SIZE_POLICY_WITH_SYSTEM_GC;
574582
}
575583

584+
protected UnsignedWord computeEdenLimit() {
585+
assert VMOperation.isGCInProgress() : "result wouldn't be consistent otherwise";
586+
return alignDown(sizes.getMaxYoungSize().subtract(sizes.getSurvivorSize().multiply(2)));
587+
}
588+
576589
private void updateCollectionEndAverages(AdaptiveWeightedAverage costAverage, AdaptivePaddedAverage pauseAverage, ReciprocalLeastSquareFit costEstimator,
577590
AdaptiveWeightedAverage intervalSeconds, GCCause cause, long mutatorNanos, long pauseNanos, UnsignedWord sizeBytes) {
578591
if (shouldUpdateStats(cause)) {
@@ -591,10 +604,4 @@ private void updateCollectionEndAverages(AdaptiveWeightedAverage costAverage, Ad
591604
costEstimator.sample(UnsignedUtils.toDouble(sizeBytes), cost);
592605
}
593606
}
594-
595-
@Override
596-
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
597-
protected long gcCount() {
598-
return minorCount + majorCount;
599-
}
600607
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,8 @@ default boolean isOutOfMemory(UnsignedWord usedBytes) {
228228
default void onMaximumHeapSizeExceeded() {
229229
throw OutOfMemoryUtil.heapSizeExceeded();
230230
}
231+
232+
@Uninterruptible(reason = "Tear-down in progress.")
233+
default void tearDown() {
234+
}
231235
}

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

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
import org.graalvm.nativeimage.ImageSingletons;
2828
import org.graalvm.word.UnsignedWord;
2929

30-
import com.oracle.svm.core.SubstrateGCOptions;
3130
import com.oracle.svm.core.heap.DynamicHeapSizeManager;
3231

3332
/**
@@ -46,26 +45,26 @@ protected long getMaximumHeapSizeOptionValue() {
4645
if (ImageSingletons.contains(DynamicHeapSizeManager.class)) {
4746
return ImageSingletons.lookup(DynamicHeapSizeManager.class).maxHeapSize().rawValue();
4847
}
49-
50-
return SubstrateGCOptions.MaxHeapSize.getValue();
48+
return super.getMaximumHeapSizeOptionValue();
5149
}
5250

5351
@Override
5452
public boolean isOutOfMemory(UnsignedWord usedBytes) {
55-
if (ImageSingletons.contains(DynamicHeapSizeManager.class)) {
56-
return ImageSingletons.lookup(DynamicHeapSizeManager.class).outOfMemory(usedBytes);
53+
boolean outOfMemory = super.isOutOfMemory(usedBytes);
54+
if (ImageSingletons.contains(DynamicHeapSizeManager.class) && outOfMemory) {
55+
outOfMemory = ImageSingletons.lookup(DynamicHeapSizeManager.class).outOfMemory(usedBytes);
56+
if (!outOfMemory) {
57+
/* No longer out-of-memory - update the heap size parameters to reflect that. */
58+
GCImpl.getPolicy().updateSizeParameters();
59+
}
5760
}
58-
59-
return super.isOutOfMemory(usedBytes);
61+
return outOfMemory;
6062
}
6163

6264
@Override
6365
public void onMaximumHeapSizeExceeded() {
6466
if (isOutOfMemory(HeapImpl.getAccounting().getUsedBytes())) {
6567
super.onMaximumHeapSizeExceeded();
66-
} else {
67-
/* No longer out-of-memory - update the heap size parameters to reflect that. */
68-
GCImpl.getPolicy().updateSizeParameters();
6968
}
7069
}
7170
}

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,11 @@ public final class GCImpl implements GC {
151151
}
152152
}
153153

154+
@Uninterruptible(reason = "Tear-down in progress.")
155+
public void tearDown() {
156+
policy.tearDown();
157+
}
158+
154159
@Override
155160
public String getName() {
156161
if (SubstrateOptions.useEpsilonGC()) {

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

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
import com.oracle.svm.core.metaspace.Metaspace;
7777
import com.oracle.svm.core.nodes.CFunctionEpilogueNode;
7878
import com.oracle.svm.core.nodes.CFunctionPrologueNode;
79+
import com.oracle.svm.core.option.NotifyGCRuntimeOptionKey;
7980
import com.oracle.svm.core.option.RuntimeOptionKey;
8081
import com.oracle.svm.core.snippets.KnownIntrinsics;
8182
import com.oracle.svm.core.thread.PlatformThreads;
@@ -211,7 +212,8 @@ public void walkObjects(ObjectVisitor visitor) {
211212
public boolean tearDown() {
212213
youngGeneration.tearDown();
213214
oldGeneration.tearDown();
214-
getChunkProvider().tearDown();
215+
chunkProvider.tearDown();
216+
gcImpl.tearDown();
215217

216218
if (Metaspace.isSupported()) {
217219
MetaspaceImpl.singleton().tearDown();
@@ -701,10 +703,19 @@ public boolean printLocationInfo(Log log, UnsignedWord value, boolean allowJavaH
701703
}
702704

703705
@Override
704-
public void optionValueChanged(RuntimeOptionKey<?> key) {
705-
if (!SubstrateUtil.HOSTED) {
706-
GCImpl.getPolicy().updateSizeParameters();
706+
public void optionValueChanged(NotifyGCRuntimeOptionKey<?> key) {
707+
if (SubstrateUtil.HOSTED || isIrrelevantForGCPolicy(key)) {
708+
return;
707709
}
710+
711+
GCImpl.getPolicy().updateSizeParameters();
712+
}
713+
714+
/** For the GC policy, mainly heap-size-related GC options are relevant. */
715+
private static boolean isIrrelevantForGCPolicy(RuntimeOptionKey<?> key) {
716+
return key == SubstrateGCOptions.DisableExplicitGC ||
717+
key == SubstrateGCOptions.PrintGC ||
718+
key == SubstrateGCOptions.VerboseGC;
708719
}
709720

710721
@Override
@@ -1008,7 +1019,7 @@ private long totalMemory() {
10081019

10091020
@Substitute
10101021
private long maxMemory() {
1011-
GCImpl.getPolicy().updateSizeParameters();
1022+
GCImpl.getPolicy().ensureSizeParametersInitialized();
10121023
return GCImpl.getPolicy().getMaximumHeapSize().rawValue();
10131024
}
10141025

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ public static UnsignedWord getMaximumHeapFree() {
124124
return Word.unsigned(SerialGCOptions.MaxHeapFree.getValue());
125125
}
126126

127+
@Fold
127128
public static int getHeapChunkHeaderPadding() {
128129
return SerialAndEpsilonGCOptions.HeapChunkHeaderPadding.getValue();
129130
}
@@ -160,15 +161,12 @@ public static UnsignedWord getLargeArrayThreshold() {
160161
return Word.unsigned(SerialAndEpsilonGCOptions.LargeArrayThreshold.getValue());
161162
}
162163

163-
/*
164-
* Zapping
165-
*/
166-
167-
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
164+
@Fold
168165
public static boolean getZapProducedHeapChunks() {
169166
return SerialAndEpsilonGCOptions.ZapChunks.getValue() || SerialAndEpsilonGCOptions.ZapProducedHeapChunks.getValue();
170167
}
171168

169+
@Fold
172170
public static boolean getZapConsumedHeapChunks() {
173171
return SerialAndEpsilonGCOptions.ZapChunks.getValue() || SerialAndEpsilonGCOptions.ZapConsumedHeapChunks.getValue();
174172
}

0 commit comments

Comments
 (0)