Skip to content

Commit e560402

Browse files
committed
Fix GC policy timers and enable timer assertions.
1 parent 6bc4f3c commit e560402

File tree

9 files changed

+106
-87
lines changed

9 files changed

+106
-87
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ public int getTenuringAge() {
320320
}
321321

322322
@Override
323-
public void onCollectionBegin(boolean completeCollection, long requestingNanoTime) {
323+
public void onCollectionBegin(boolean completeCollection, long beginNanoTime) {
324324
// Capture the fraction of bytes in aligned chunks at the start to include all allocated
325325
// (also dead) objects, because we use it to reserve aligned chunks for future allocations
326326
UnsignedWord youngChunkBytes = GCImpl.getAccounting().getYoungChunkBytesBefore();

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

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
import org.graalvm.word.UnsignedWord;
3030

31+
import com.oracle.svm.core.Isolates;
3132
import com.oracle.svm.core.Uninterruptible;
3233
import com.oracle.svm.core.heap.GCCause;
3334
import com.oracle.svm.core.util.BasedOnJDKFile;
@@ -356,6 +357,10 @@ private double decayingGcCost() { // decaying_gc_cost and decaying_major_gc_cost
356357
double decayedMajorGcCost = majorGcCost();
357358
double avgMajorInterval = avgMajorIntervalSeconds.getAverage();
358359
if (USE_ADAPTIVE_SIZE_DECAY_MAJOR_GC_COST && ADAPTIVE_SIZE_MAJOR_GC_DECAY_TIME_SCALE > 0 && avgMajorInterval > 0) {
360+
/*
361+
* This seems pointless or flawed for major GCs because this method is called at the end
362+
* when majorTimer has only just been restarted.
363+
*/
359364
double secondsSinceMajor = secondsSinceMajorGc();
360365
if (secondsSinceMajor > 0 && secondsSinceMajor > ADAPTIVE_SIZE_MAJOR_GC_DECAY_TIME_SCALE * avgMajorInterval) {
361366
double decayed = decayedMajorGcCost * (ADAPTIVE_SIZE_MAJOR_GC_DECAY_TIME_SCALE * avgMajorInterval) / secondsSinceMajor;
@@ -393,24 +398,31 @@ private static UnsignedWord spaceIncrement(UnsignedWord curSize, UnsignedWord pe
393398
return curSize.unsignedDivide(100).multiply(percentChange);
394399
}
395400

401+
/**
402+
* Should not be called during a major collection itself because then, {@link #majorTimer} is
403+
* repurposed to measure collection time (rather than time between collections).
404+
*/
396405
private double secondsSinceMajorGc() { // time_since_major_gc
397-
return TimeUtils.nanosToSecondsDouble(System.nanoTime() - majorTimer.startedNanos());
406+
return TimeUtils.nanosToSecondsDouble(System.nanoTime() - majorTimer.lastStartedNanoTime());
398407
}
399408

400409
@Override
401-
public void onCollectionBegin(boolean completeCollection, long requestingNanoTime) { // {major,minor}_collection_begin
410+
public void onCollectionBegin(boolean completeCollection, long beginNanoTime) { // {major,minor}_collection_begin
402411
Timer timer = completeCollection ? majorTimer : minorTimer;
403-
timer.stopAt(requestingNanoTime);
412+
if (!timer.wasStartedAtLeastOnce()) {
413+
long origin = Isolates.isStartTimeAssigned() ? Isolates.getStartTimeNanos() : beginNanoTime;
414+
timer.startAt(origin);
415+
}
416+
timer.stopAt(beginNanoTime);
404417
if (completeCollection) {
405-
latestMajorMutatorIntervalNanos = timer.totalNanos();
418+
latestMajorMutatorIntervalNanos = timer.lastIntervalNanos();
406419
} else {
407-
latestMinorMutatorIntervalNanos = timer.totalNanos();
420+
latestMinorMutatorIntervalNanos = timer.lastIntervalNanos();
408421
}
409422

410-
timer.reset();
411423
timer.start(); // measure collection pause
412424

413-
super.onCollectionBegin(completeCollection, requestingNanoTime);
425+
super.onCollectionBegin(completeCollection, beginNanoTime);
414426
}
415427

416428
@Override
@@ -420,13 +432,13 @@ public void onCollectionEnd(boolean completeCollection, GCCause cause) { // {maj
420432

421433
if (completeCollection) {
422434
updateCollectionEndAverages(avgMajorGcCost, avgMajorPause, majorCostEstimator, avgMajorIntervalSeconds,
423-
cause, latestMajorMutatorIntervalNanos, timer.totalNanos(), promoSize);
435+
cause, latestMajorMutatorIntervalNanos, timer.lastIntervalNanos(), promoSize);
424436
majorCount++;
425437
minorCountSinceMajorCollection = 0;
426438

427439
} else {
428440
updateCollectionEndAverages(avgMinorGcCost, avgMinorPause, minorCostEstimator, null,
429-
cause, latestMinorMutatorIntervalNanos, timer.totalNanos(), edenSize);
441+
cause, latestMinorMutatorIntervalNanos, timer.lastIntervalNanos(), edenSize);
430442
minorCount++;
431443
minorCountSinceMajorCollection++;
432444

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ public int getTenuringAge() {
196196
}
197197

198198
@Override
199-
public void onCollectionBegin(boolean completeCollection, long requestingNanoTime) {
199+
public void onCollectionBegin(boolean completeCollection, long beginNanoTime) {
200200
}
201201

202202
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ static boolean shouldCollectYoungGenSeparately(boolean defaultValue) {
209209
int getTenuringAge();
210210

211211
/** Called at the beginning of a collection, in the safepoint operation. */
212-
void onCollectionBegin(boolean completeCollection, long requestingNanoTime);
212+
void onCollectionBegin(boolean completeCollection, long beginNanoTime);
213213

214214
/** Called before the end of a collection, in the safepoint operation. */
215215
void onCollectionEnd(boolean completeCollection, GCCause cause);

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

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,23 @@ private void collectOperation(CollectionVMOperationData data) {
238238
assert getCollectionEpoch().equal(data.getRequestingEpoch()) ||
239239
data.getForceFullGC() && GCImpl.getAccounting().getCompleteCollectionCount() == data.getCompleteCollectionCount() : "unnecessary GC?";
240240

241-
timers.mutator.stopAt(data.getRequestingNanoTime());
241+
/*
242+
* We use the time of the GC request as the beginning of the collection because it includes
243+
* the delay of entering a safepoint, which we want the policy to consider for GC cost.
244+
* Other VM operations can run in between, but we expect them to have insignificant impact.
245+
*/
246+
long beginNanoTime = data.getRequestingNanoTime();
247+
if (getCollectionEpoch().notEqual(data.getRequestingEpoch())) {
248+
/* Another GC happened since the GC request, use the current time instead. */
249+
beginNanoTime = System.nanoTime();
250+
}
251+
if (!timers.mutator.wasStartedAtLeastOnce()) {
252+
long origin = Isolates.isStartTimeAssigned() ? Isolates.getStartTimeNanos() : beginNanoTime;
253+
timers.mutator.startAt(origin);
254+
}
255+
timers.mutator.stopAt(beginNanoTime);
242256
timers.resetAllExceptMutator();
257+
243258
/* The type of collection will be determined later on. */
244259
completeCollection = false;
245260

@@ -256,7 +271,7 @@ assert getCollectionEpoch().equal(data.getRequestingEpoch()) ||
256271

257272
verifyHeap(Before);
258273

259-
boolean outOfMemory = collectImpl(cause, data.getRequestingNanoTime(), data.getForceFullGC());
274+
boolean outOfMemory = collectImpl(cause, beginNanoTime, data.getForceFullGC());
260275
data.setOutOfMemory(outOfMemory);
261276

262277
verifyHeap(After);
@@ -277,18 +292,17 @@ assert getCollectionEpoch().equal(data.getRequestingEpoch()) ||
277292
timers.mutator.start();
278293
}
279294

280-
private boolean collectImpl(GCCause cause, long requestingNanoTime, boolean forceFullGC) {
295+
private boolean collectImpl(GCCause cause, long beginNanoTime, boolean forceFullGC) {
281296
boolean outOfMemory;
282297
long startTicks = JfrTicks.elapsedTicks();
283298
try {
284-
outOfMemory = doCollectImpl(cause, requestingNanoTime, forceFullGC, false);
299+
outOfMemory = doCollectImpl(cause, beginNanoTime, forceFullGC, false);
285300
if (outOfMemory) {
286-
// Avoid running out of memory with a full GC that reclaims softly reachable
287-
// objects
301+
// Avoid running out of memory with a full GC that reclaims softly reachable objects
288302
ReferenceObjectProcessing.setSoftReferencesAreWeak(true);
289303
try {
290304
verifyHeap(During);
291-
outOfMemory = doCollectImpl(cause, requestingNanoTime, true, true);
305+
outOfMemory = doCollectImpl(cause, System.nanoTime(), true, true);
292306
} finally {
293307
ReferenceObjectProcessing.setSoftReferencesAreWeak(false);
294308
}
@@ -299,7 +313,7 @@ private boolean collectImpl(GCCause cause, long requestingNanoTime, boolean forc
299313
return outOfMemory;
300314
}
301315

302-
private boolean doCollectImpl(GCCause cause, long requestingNanoTime, boolean forceFullGC, boolean forceNoIncremental) {
316+
private boolean doCollectImpl(GCCause cause, long initialBeginNanoTime, boolean forceFullGC, boolean forceNoIncremental) {
303317
checkSanityBeforeCollection();
304318

305319
ChunkBasedCommittedMemoryProvider.get().beforeGarbageCollection();
@@ -310,19 +324,21 @@ private boolean doCollectImpl(GCCause cause, long requestingNanoTime, boolean fo
310324
if (incremental) {
311325
long startTicks = JfrGCEvents.startGCPhasePause();
312326
try {
313-
outOfMemory = doCollectOnce(cause, requestingNanoTime, false, false);
327+
outOfMemory = doCollectOnce(cause, initialBeginNanoTime, false, false);
314328
} finally {
315329
JfrGCEvents.emitGCPhasePauseEvent(getCollectionEpoch(), "Incremental GC", startTicks);
316330
}
317331
}
318332
if (!incremental || outOfMemory || forceFullGC || policy.shouldCollectCompletely(incremental)) {
319-
if (incremental) { // uncommit unaligned chunks
320-
ChunkBasedCommittedMemoryProvider.get().uncommitUnusedMemory();
333+
long beginNanoTime = initialBeginNanoTime;
334+
if (incremental) {
335+
beginNanoTime = System.nanoTime();
336+
ChunkBasedCommittedMemoryProvider.get().uncommitUnusedMemory(); // unaligned chunks
321337
verifyHeap(During);
322338
}
323339
long startTicks = JfrGCEvents.startGCPhasePause();
324340
try {
325-
outOfMemory = doCollectOnce(cause, requestingNanoTime, true, incremental);
341+
outOfMemory = doCollectOnce(cause, beginNanoTime, true, incremental);
326342
} finally {
327343
JfrGCEvents.emitGCPhasePauseEvent(getCollectionEpoch(), "Full GC", startTicks);
328344
}
@@ -335,13 +351,13 @@ private boolean doCollectImpl(GCCause cause, long requestingNanoTime, boolean fo
335351
return outOfMemory;
336352
}
337353

338-
private boolean doCollectOnce(GCCause cause, long requestingNanoTime, boolean complete, boolean followsIncremental) {
354+
private boolean doCollectOnce(GCCause cause, long beginNanoTime, boolean complete, boolean followsIncremental) {
339355
assert !followsIncremental || complete : "An incremental collection cannot be followed by another incremental collection";
340356
assert !completeCollection || complete : "After a complete collection, no further incremental collections may happen";
341357
completeCollection = complete;
342358

343359
accounting.beforeCollectOnce(completeCollection);
344-
policy.onCollectionBegin(completeCollection, requestingNanoTime);
360+
policy.onCollectionBegin(completeCollection, beginNanoTime);
345361

346362
doCollectCore(!complete);
347363
if (complete) {

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,9 @@ protected UnsignedWord getYoungSizeLimit(UnsignedWord maxHeap) {
110110
}
111111

112112
@Override
113-
public void onCollectionBegin(boolean completeCollection, long requestingNanoTime) {
113+
public void onCollectionBegin(boolean completeCollection, long beginNanoTime) {
114114
sizeBefore = GCImpl.getChunkBytes();
115-
super.onCollectionBegin(completeCollection, requestingNanoTime);
115+
super.onCollectionBegin(completeCollection, beginNanoTime);
116116
}
117117

118118
@Override

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public boolean shouldCollectCompletely(boolean followingIncrementalCollection) {
8585
}
8686

8787
@Override
88-
public void onCollectionBegin(boolean completeCollection, long requestingNanoTime) {
88+
public void onCollectionBegin(boolean completeCollection, long beginNanoTime) {
8989
}
9090

9191
@Override

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/Isolates.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ public class Isolates {
7676
/* Only used if SpawnIsolates is disabled. */
7777
private static final CGlobalData<Pointer> SINGLE_ISOLATE_ALREADY_CREATED = CGlobalDataFactory.createWord();
7878

79+
private static boolean startTimesAssigned;
7980
private static long startTimeNanos;
8081
private static long initDoneTimeMillis;
8182
private static long isolateId = -1;
@@ -110,31 +111,35 @@ public static void assignIsolateId(boolean isFirstIsolate) {
110111
}
111112

112113
public static void assignStartTime() {
113-
assert startTimeNanos == 0 : startTimeNanos;
114-
assert initDoneTimeMillis == 0 : initDoneTimeMillis;
114+
assert !startTimesAssigned;
115115
startTimeNanos = System.nanoTime();
116116
initDoneTimeMillis = TimeUtils.currentTimeMillis();
117+
startTimesAssigned = true;
117118
}
118119

119120
/** Epoch-based timestamp. If possible, {@link #getStartTimeNanos()} should be used instead. */
120121
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
121122
public static long getInitDoneTimeMillis() {
122-
assert initDoneTimeMillis != 0;
123+
assert startTimesAssigned;
123124
return initDoneTimeMillis;
124125
}
125126

126127
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
127128
public static long getUptimeMillis() {
128-
assert startTimeNanos != 0;
129-
return TimeUtils.millisSinceNanos(startTimeNanos);
129+
return TimeUtils.millisSinceNanos(getStartTimeNanos());
130130
}
131131

132132
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
133133
public static long getStartTimeNanos() {
134-
assert startTimeNanos != 0;
134+
assert startTimesAssigned;
135135
return startTimeNanos;
136136
}
137137

138+
@Uninterruptible(reason = CALLED_FROM_UNINTERRUPTIBLE_CODE, mayBeInlined = true)
139+
public static boolean isStartTimeAssigned() {
140+
return startTimesAssigned;
141+
}
142+
138143
/**
139144
* Gets an identifier for the current isolate that is guaranteed to be unique for the first
140145
* {@code 2^64 - 1} isolates in the process.

0 commit comments

Comments
 (0)