Skip to content

Commit 5e5953b

Browse files
[GR-48343] [GR-48359] Add support for JFR events AllocationRequiringGC and SystemGC.
PullRequest: graal/15416
2 parents 3507c6e + a141887 commit 5e5953b

File tree

19 files changed

+326
-46
lines changed

19 files changed

+326
-46
lines changed

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ This changelog summarizes major changes to GraalVM Native Image.
44

55
## GraalVM for JDK 22 (Internal Version 24.0.0)
66
* (GR-48304) Red Hat added support for the JFR event ThreadAllocationStatistics.
7+
* (GR-48343) Red Hat added support for the JFR events AllocationRequiringGC and SystemGC.
78

89
## GraalVM for JDK 21 (Internal Version 23.1.0)
910
* (GR-35746) Lower the default aligned chunk size from 1 MB to 512 KB for the serial and epsilon GCs, reducing memory usage and image size in many cases.

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535

3636
import com.oracle.svm.core.SubstrateGCOptions;
3737
import com.oracle.svm.core.Uninterruptible;
38-
import com.oracle.svm.core.heap.GCCause;
3938
import com.oracle.svm.core.heap.PhysicalMemory;
4039
import com.oracle.svm.core.heap.ReferenceAccess;
4140
import com.oracle.svm.core.jdk.UninterruptibleUtils;
@@ -96,8 +95,9 @@ public boolean shouldCollectOnAllocation() {
9695
}
9796

9897
@Override
99-
public boolean shouldCollectOnRequest(GCCause cause, boolean fullGC) {
100-
return cause == GCCause.JavaLangSystemGC && !SubstrateGCOptions.DisableExplicitGC.getValue();
98+
public boolean shouldCollectOnHint(boolean fullGC) {
99+
/* Collection hints are not supported. */
100+
return false;
101101
}
102102

103103
@Fold

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,9 @@ public boolean shouldCollectOnAllocation() {
6363
}
6464

6565
@Override
66-
public boolean shouldCollectOnRequest(GCCause cause, boolean fullGC) {
67-
return cause == GCCause.JavaLangSystemGC && !SubstrateGCOptions.DisableExplicitGC.getValue();
66+
public boolean shouldCollectOnHint(boolean fullGC) {
67+
/* Collection hints are not supported. */
68+
return false;
6869
}
6970

7071
@Override

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

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,11 +122,10 @@ static boolean shouldCollectYoungGenSeparately(boolean defaultValue) {
122122
boolean shouldCollectOnAllocation();
123123

124124
/**
125-
* Return true if a user-requested GC (e.g., call to {@link System#gc()} or
126-
* {@link org.graalvm.compiler.serviceprovider.GraalServices#notifyLowMemoryPoint(boolean)})
127-
* should be performed.
125+
* Called when an application provides a hint to the GC that it might be a good time to do a
126+
* collection. Returns true if the GC decides to do a collection.
128127
*/
129-
boolean shouldCollectOnRequest(GCCause cause, boolean fullGC);
128+
boolean shouldCollectOnHint(boolean fullGC);
130129

131130
/**
132131
* At a safepoint, decides whether to do a complete collection (returning {@code true}) or an

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ public long getIncrementalCollectionTotalNanos() {
7878
return incrementalCollectionTotalNanos;
7979
}
8080

81+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
8182
public long getCompleteCollectionCount() {
8283
return completeCollectionCount;
8384
}

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

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@
8181
import com.oracle.svm.core.jdk.RuntimeSupport;
8282
import com.oracle.svm.core.jfr.JfrGCWhen;
8383
import com.oracle.svm.core.jfr.JfrTicks;
84+
import com.oracle.svm.core.jfr.events.AllocationRequiringGCEvent;
8485
import com.oracle.svm.core.log.Log;
8586
import com.oracle.svm.core.os.CommittedMemoryProvider;
8687
import com.oracle.svm.core.snippets.ImplicitExceptions;
@@ -144,12 +145,13 @@ public void collect(GCCause cause) {
144145
collect(cause, false);
145146
}
146147

147-
public void maybeCollectOnAllocation() {
148+
public void maybeCollectOnAllocation(UnsignedWord allocationSize) {
148149
boolean outOfMemory = false;
149150
if (hasNeverCollectPolicy()) {
150151
UnsignedWord edenUsed = HeapImpl.getAccounting().getEdenUsedBytes();
151152
outOfMemory = edenUsed.aboveThan(GCImpl.getPolicy().getMaximumHeapSize());
152153
} else if (getPolicy().shouldCollectOnAllocation()) {
154+
AllocationRequiringGCEvent.emit(getCollectionEpoch(), allocationSize);
153155
outOfMemory = collectWithoutAllocating(GenScavengeGCCause.OnAllocation, false);
154156
}
155157
if (outOfMemory) {
@@ -158,9 +160,9 @@ public void maybeCollectOnAllocation() {
158160
}
159161

160162
@Override
161-
public void maybeCauseUserRequestedCollection(GCCause cause, boolean fullGC) {
162-
if (policy.shouldCollectOnRequest(cause, fullGC)) {
163-
collect(cause, fullGC);
163+
public void collectionHint(boolean fullGC) {
164+
if (policy.shouldCollectOnHint(fullGC)) {
165+
collect(GCCause.HintedGC, fullGC);
164166
}
165167
}
166168

@@ -183,6 +185,7 @@ boolean collectWithoutAllocating(GCCause cause, boolean forceFullGC) {
183185
UnmanagedMemoryUtil.fill((Pointer) data, WordFactory.unsigned(size), (byte) 0);
184186
data.setCauseId(cause.getId());
185187
data.setRequestingEpoch(getCollectionEpoch());
188+
data.setCompleteCollectionCount(GCImpl.getAccounting().getCompleteCollectionCount());
186189
data.setRequestingNanoTime(System.nanoTime());
187190
data.setForceFullGC(forceFullGC);
188191
enqueueCollectOperation(data);
@@ -197,7 +200,8 @@ private void enqueueCollectOperation(CollectionVMOperationData data) {
197200
/** The body of the VMOperation to do the collection. */
198201
private void collectOperation(CollectionVMOperationData data) {
199202
assert VMOperation.isGCInProgress();
200-
assert getCollectionEpoch().equal(data.getRequestingEpoch());
203+
assert getCollectionEpoch().equal(data.getRequestingEpoch()) ||
204+
data.getForceFullGC() && GCImpl.getAccounting().getCompleteCollectionCount() == data.getCompleteCollectionCount() : "unnecessary GC?";
201205

202206
timers.mutator.closeAt(data.getRequestingNanoTime());
203207
timers.resetAllExceptMutator();
@@ -1205,7 +1209,12 @@ private static void collect(CollectionVMOperationData data) {
12051209
@Override
12061210
protected boolean hasWork(NativeVMOperationData data) {
12071211
CollectionVMOperationData d = (CollectionVMOperationData) data;
1208-
return HeapImpl.getGCImpl().getCollectionEpoch().equal(d.getRequestingEpoch());
1212+
if (d.getForceFullGC()) {
1213+
/* Skip if another full GC happened in the meanwhile. */
1214+
return GCImpl.getAccounting().getCompleteCollectionCount() == d.getCompleteCollectionCount();
1215+
}
1216+
/* Skip if any other GC happened in the meanwhile. */
1217+
return GCImpl.getGCImpl().getCollectionEpoch().equal(d.getRequestingEpoch());
12091218
}
12101219
}
12111220

@@ -1235,6 +1244,12 @@ private interface CollectionVMOperationData extends NativeVMOperationData {
12351244
@RawField
12361245
void setForceFullGC(boolean value);
12371246

1247+
@RawField
1248+
long getCompleteCollectionCount();
1249+
1250+
@RawField
1251+
void setCompleteCollectionCount(long value);
1252+
12381253
@RawField
12391254
boolean getOutOfMemory();
12401255

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

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunk;
4949
import com.oracle.svm.core.SubstrateDiagnostics.DiagnosticThunkRegistry;
5050
import com.oracle.svm.core.SubstrateDiagnostics.ErrorContext;
51+
import com.oracle.svm.core.SubstrateGCOptions;
5152
import com.oracle.svm.core.SubstrateOptions;
5253
import com.oracle.svm.core.SubstrateUtil;
5354
import com.oracle.svm.core.Uninterruptible;
@@ -73,6 +74,8 @@
7374
import com.oracle.svm.core.heap.RuntimeCodeInfoGCSupport;
7475
import com.oracle.svm.core.hub.DynamicHub;
7576
import com.oracle.svm.core.jdk.UninterruptibleUtils.AtomicReference;
77+
import com.oracle.svm.core.jfr.JfrTicks;
78+
import com.oracle.svm.core.jfr.events.SystemGCEvent;
7679
import com.oracle.svm.core.locks.VMCondition;
7780
import com.oracle.svm.core.locks.VMMutex;
7881
import com.oracle.svm.core.log.Log;
@@ -941,6 +944,10 @@ private long maxMemory() {
941944

942945
@Substitute
943946
private void gc() {
944-
GCImpl.getGCImpl().maybeCauseUserRequestedCollection(GCCause.JavaLangSystemGC, true);
947+
if (!SubstrateGCOptions.DisableExplicitGC.getValue()) {
948+
long startTicks = JfrTicks.elapsedTicks();
949+
GCImpl.getGCImpl().collectCompletely(GCCause.JavaLangSystemGC);
950+
SystemGCEvent.emit(startTicks, false);
951+
}
945952
}
946953
}

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

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -73,20 +73,18 @@ public static final class Options {
7373
* size is lower but the hinted GC is more often.
7474
*/
7575
@Override
76-
public boolean shouldCollectOnRequest(GCCause cause, boolean fullGC) {
77-
if (cause == GCCause.HintedGC) {
78-
guaranteeSizeParametersInitialized();
79-
UnsignedWord edenUsedBytes = HeapImpl.getAccounting().getEdenUsedBytes();
80-
if (fullGC) {
81-
// For full GC request, we slightly lower the threshold to increase their
82-
// probability to be performed, as they are supposed to be issued at the lowest
83-
// memory usage point.
84-
edenUsedBytes = edenUsedBytes.add(FULL_GC_BONUS);
85-
}
86-
return edenUsedBytes.aboveOrEqual(WordFactory.unsigned(Options.ExpectedEdenSize.getValue())) ||
87-
(UnsignedUtils.toDouble(edenUsedBytes) / UnsignedUtils.toDouble(edenSize) >= Options.UsedEdenProportionThreshold.getValue());
76+
public boolean shouldCollectOnHint(boolean fullGC) {
77+
guaranteeSizeParametersInitialized();
78+
UnsignedWord edenUsedBytes = HeapImpl.getAccounting().getEdenUsedBytes();
79+
if (fullGC) {
80+
/*
81+
* For full GC request, we slightly lower the threshold to increase their probability to
82+
* be performed, as they are supposed to be issued at the lowest memory usage point.
83+
*/
84+
edenUsedBytes = edenUsedBytes.add(FULL_GC_BONUS);
8885
}
89-
return super.shouldCollectOnRequest(cause, fullGC);
86+
return edenUsedBytes.aboveOrEqual(WordFactory.unsigned(Options.ExpectedEdenSize.getValue())) ||
87+
(UnsignedUtils.toDouble(edenUsedBytes) / UnsignedUtils.toDouble(edenSize) >= Options.UsedEdenProportionThreshold.getValue());
9088
}
9189

9290
@Override

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -230,14 +230,15 @@ private static Object slowPathNewInstance(Word objectHeader) {
230230
private static Object slowPathNewInstanceWithoutAllocating(DynamicHub hub) {
231231
DeoptTester.disableDeoptTesting();
232232
long startTicks = JfrTicks.elapsedTicks();
233+
UnsignedWord size = LayoutEncoding.getPureInstanceAllocationSize(hub.getLayoutEncoding());
233234
try {
234235
HeapImpl.exitIfAllocationDisallowed("ThreadLocalAllocation.slowPathNewInstanceWithoutAllocating", DynamicHub.toClass(hub).getName());
235-
GCImpl.getGCImpl().maybeCollectOnAllocation();
236+
GCImpl.getGCImpl().maybeCollectOnAllocation(size);
236237

237238
AlignedHeader newTlab = HeapImpl.getChunkProvider().produceAlignedChunk();
238-
return allocateInstanceInNewTlab(hub, newTlab);
239+
return allocateInstanceInNewTlab(hub, size, newTlab);
239240
} finally {
240-
ObjectAllocationInNewTLABEvent.emit(startTicks, hub, LayoutEncoding.getPureInstanceAllocationSize(hub.getLayoutEncoding()), HeapParameters.getAlignedHeapChunkSize());
241+
ObjectAllocationInNewTLABEvent.emit(startTicks, hub, size, HeapParameters.getAlignedHeapChunkSize());
241242
DeoptTester.enableDeoptTesting();
242243
}
243244
}
@@ -296,7 +297,7 @@ private static Object slowPathNewArrayLikeObject0(DynamicHub hub, int length, Un
296297
UnsignedWord tlabSize = HeapParameters.getAlignedHeapChunkSize();
297298
try {
298299
HeapImpl.exitIfAllocationDisallowed("ThreadLocalAllocation.slowPathNewArrayOrPodWithoutAllocating", DynamicHub.toClass(hub).getName());
299-
GCImpl.getGCImpl().maybeCollectOnAllocation();
300+
GCImpl.getGCImpl().maybeCollectOnAllocation(size);
300301

301302
if (size.aboveOrEqual(HeapParameters.getLargeArrayThreshold())) {
302303
/* Large arrays go into their own unaligned chunk. */
@@ -305,10 +306,12 @@ private static Object slowPathNewArrayLikeObject0(DynamicHub hub, int length, Un
305306
tlabSize = UnalignedHeapChunk.getChunkSizeForObject(size);
306307
return allocateLargeArrayLikeObjectInNewTlab(hub, length, size, newTlabChunk, needsZeroing, podReferenceMap);
307308
}
308-
/* Small arrays go into the regular aligned chunk. */
309309

310-
// We might have allocated in the caller and acquired a TLAB with enough space already
311-
// (but we need to check in an uninterruptible method to be safe)
310+
/*
311+
* Small arrays go into the regular aligned chunk. We might have allocated in the caller
312+
* and acquired a TLAB with enough space already (but we need to check in an
313+
* uninterruptible method to be safe).
314+
*/
312315
Object array = allocateSmallArrayLikeObjectInCurrentTlab(hub, length, size, podReferenceMap);
313316
if (array == null) { // We need a new chunk.
314317
AlignedHeader newTlabChunk = HeapImpl.getChunkProvider().produceAlignedChunk();
@@ -322,8 +325,8 @@ private static Object slowPathNewArrayLikeObject0(DynamicHub hub, int length, Un
322325
}
323326

324327
@Uninterruptible(reason = "Holds uninitialized memory.")
325-
private static Object allocateInstanceInNewTlab(DynamicHub hub, AlignedHeader newTlabChunk) {
326-
UnsignedWord size = LayoutEncoding.getPureInstanceAllocationSize(hub.getLayoutEncoding());
328+
private static Object allocateInstanceInNewTlab(DynamicHub hub, UnsignedWord size, AlignedHeader newTlabChunk) {
329+
assert size.equal(LayoutEncoding.getPureInstanceAllocationSize(hub.getLayoutEncoding()));
327330
Pointer memory = allocateRawMemoryInNewTlab(size, newTlabChunk);
328331
return FormatObjectNode.formatObject(memory, DynamicHub.toClass(hub), false, FillContent.WITH_ZEROES, true);
329332
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/heap/GC.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,15 @@ public interface GC {
3131
/** Cause a full collection. */
3232
void collectCompletely(GCCause cause);
3333

34+
/**
35+
* Notify the GC that it might be a good time to do a collection. The final decision is up to
36+
* the GC and its policy.
37+
*/
38+
void collectionHint(boolean fullGC);
39+
3440
/** Human-readable name. */
3541
String getName();
3642

3743
/** Human-readable default heap size. */
3844
String getDefaultMaxHeapSize();
39-
40-
/** Issue an optional GC request. */
41-
default void maybeCauseUserRequestedCollection(@SuppressWarnings("unused") GCCause cause, @SuppressWarnings("unused") boolean fullGC) {
42-
}
4345
}

0 commit comments

Comments
 (0)