Skip to content

Commit b8485c3

Browse files
[GR-26814] [GR-36764] Implementation of JFR phase pause events for the serial GC, cleanups, and bugfixes.
PullRequest: graal/10983
2 parents 2f56860 + 8d9784e commit b8485c3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+469
-288
lines changed

substratevm/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ This changelog summarizes major changes to GraalVM Native Image.
55
## Version 22.1.0
66
* (GR-35898) Improved handling of static synchronized methods: the lock is no longer stored in the secondary monitor map, but in the mutable DynamicHubCompanion object.
77
* Remove support for JDK8. As a result, `JDK8OrEarlier` and `JDK11OrLater` have been deprecated and will be removed in a future release.
8+
* (GR-26814) Red Hat added support for the GC pause-related JFR events (`GCPhasePause`, `GCPhasePauseLevel*`) to the serial GC.
89

910
## Version 22.0.0
1011
* (GR-33930) Decouple HostedOptionParser setup from classpath/modulepath scanning (use ServiceLoader for collecting options).

substratevm/mx.substratevm/suite.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1107,7 +1107,7 @@
11071107
"com.oracle.svm.core", # Uses of com.oracle.svm.core.TypeResult
11081108
"com.oracle.svm.core.util", # Uses of com.oracle.svm.core.util.VMError
11091109
"com.oracle.svm.core.jni", # Uses of com.oracle.svm.core.jni.JNIRuntimeAccess
1110-
"com.oracle.svm.core.jfr", # Uses of com.oracle.svm.core.jfr.JfrEnabled
1110+
"com.oracle.svm.core.jfr", # Uses of com.oracle.svm.core.jfr.HasJfrSupport
11111111
"com.oracle.svm.hosted to java.base",
11121112
"com.oracle.svm.hosted.agent to java.instrument",
11131113
"com.oracle.svm.truffle.api to org.graalvm.truffle",

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

Lines changed: 71 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2020, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -209,15 +209,21 @@ private boolean collectImpl(GCCause cause, long requestingNanoTime, boolean forc
209209

210210
NoAllocationVerifier nav = noAllocationVerifier.open();
211211
try {
212-
outOfMemory = doCollectImpl(cause, requestingNanoTime, forceFullGC, false);
213-
if (outOfMemory) {
214-
// Avoid running out of memory with a full GC that reclaims softly reachable objects
215-
ReferenceObjectProcessing.setSoftReferencesAreWeak(true);
216-
try {
217-
outOfMemory = doCollectImpl(cause, requestingNanoTime, true, true);
218-
} finally {
219-
ReferenceObjectProcessing.setSoftReferencesAreWeak(false);
212+
long startTicks = JfrGCEventSupport.startGCPhasePause();
213+
try {
214+
outOfMemory = doCollectImpl(cause, requestingNanoTime, forceFullGC, false);
215+
if (outOfMemory) {
216+
// Avoid running out of memory with a full GC that reclaims softly reachable
217+
// objects
218+
ReferenceObjectProcessing.setSoftReferencesAreWeak(true);
219+
try {
220+
outOfMemory = doCollectImpl(cause, requestingNanoTime, true, true);
221+
} finally {
222+
ReferenceObjectProcessing.setSoftReferencesAreWeak(false);
223+
}
220224
}
225+
} finally {
226+
JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), cause.getName(), startTicks);
221227
}
222228
} finally {
223229
nav.close();
@@ -232,14 +238,25 @@ private boolean doCollectImpl(GCCause cause, long requestingNanoTime, boolean fo
232238

233239
boolean incremental = !forceNoIncremental && !policy.shouldCollectCompletely(false);
234240
boolean outOfMemory = false;
241+
235242
if (incremental) {
236-
outOfMemory = doCollectOnce(cause, requestingNanoTime, false, false);
243+
long startTicks = JfrGCEventSupport.startGCPhasePause();
244+
try {
245+
outOfMemory = doCollectOnce(cause, requestingNanoTime, false, false);
246+
} finally {
247+
JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Incremental GC", startTicks);
248+
}
237249
}
238250
if (!incremental || outOfMemory || forceFullGC || policy.shouldCollectCompletely(incremental)) {
239251
if (incremental) { // uncommit unaligned chunks
240252
CommittedMemoryProvider.get().afterGarbageCollection();
241253
}
242-
outOfMemory = doCollectOnce(cause, requestingNanoTime, true, incremental);
254+
long startTicks = JfrGCEventSupport.startGCPhasePause();
255+
try {
256+
outOfMemory = doCollectOnce(cause, requestingNanoTime, true, incremental);
257+
} finally {
258+
JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Full GC", startTicks);
259+
}
243260
}
244261

245262
HeapImpl.getChunkProvider().freeExcessAlignedChunks();
@@ -496,13 +513,19 @@ public boolean isCompleteCollection() {
496513
/** Scavenge, either from dirty roots or from all roots, and process discovered references. */
497514
private void scavenge(boolean incremental) {
498515
GreyToBlackObjRefVisitor.Counters counters = greyToBlackObjRefVisitor.openCounters();
516+
long startTicks;
499517
try {
500518
Timer rootScanTimer = timers.rootScan.open();
501519
try {
502-
if (incremental) {
503-
cheneyScanFromDirtyRoots();
504-
} else {
505-
cheneyScanFromRoots();
520+
startTicks = JfrGCEventSupport.startGCPhasePause();
521+
try {
522+
if (incremental) {
523+
cheneyScanFromDirtyRoots();
524+
} else {
525+
cheneyScanFromRoots();
526+
}
527+
} finally {
528+
JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), incremental ? "Incremental Scan Roots" : "Scan Roots", startTicks);
506529
}
507530
} finally {
508531
rootScanTimer.close();
@@ -516,37 +539,58 @@ private void scavenge(boolean incremental) {
516539
* operation. To avoid side-effects between the code cache cleaning and the GC
517540
* core, it is crucial that all the GC core work finished before.
518541
*/
519-
cleanRuntimeCodeCache();
542+
startTicks = JfrGCEventSupport.startGCPhasePause();
543+
try {
544+
cleanRuntimeCodeCache();
545+
} finally {
546+
JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Clean Runtime CodeCache", startTicks);
547+
}
520548
} finally {
521549
cleanCodeCacheTimer.close();
522550
}
523551
}
524552

525553
Timer referenceObjectsTimer = timers.referenceObjects.open();
526554
try {
527-
Reference<?> newlyPendingList = ReferenceObjectProcessing.processRememberedReferences();
528-
HeapImpl.getHeapImpl().addToReferencePendingList(newlyPendingList);
555+
startTicks = JfrGCEventSupport.startGCPhasePause();
556+
try {
557+
Reference<?> newlyPendingList = ReferenceObjectProcessing.processRememberedReferences();
558+
HeapImpl.getHeapImpl().addToReferencePendingList(newlyPendingList);
559+
} finally {
560+
JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Process Remembered References", startTicks);
561+
}
529562
} finally {
530563
referenceObjectsTimer.close();
531564
}
532565

533566
Timer releaseSpacesTimer = timers.releaseSpaces.open();
534567
try {
535568
assert chunkReleaser.isEmpty();
536-
releaseSpaces();
569+
startTicks = JfrGCEventSupport.startGCPhasePause();
570+
try {
571+
releaseSpaces();
537572

538-
/*
539-
* Do not uncommit any aligned chunks yet if we just did an incremental GC so if we
540-
* decide to do a full GC next, we can reuse the chunks for copying live old objects
541-
* with fewer chunk allocations. In either case, excess chunks are released later.
542-
*/
543-
boolean keepAllAlignedChunks = incremental;
544-
chunkReleaser.release(keepAllAlignedChunks);
573+
/*
574+
* Do not uncommit any aligned chunks yet if we just did an incremental GC so if
575+
* we decide to do a full GC next, we can reuse the chunks for copying live old
576+
* objects with fewer chunk allocations. In either case, excess chunks are
577+
* released later.
578+
*/
579+
boolean keepAllAlignedChunks = incremental;
580+
chunkReleaser.release(keepAllAlignedChunks);
581+
} finally {
582+
JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Release Spaces", startTicks);
583+
}
545584
} finally {
546585
releaseSpacesTimer.close();
547586
}
548587

549-
swapSpaces();
588+
startTicks = JfrGCEventSupport.startGCPhasePause();
589+
try {
590+
swapSpaces();
591+
} finally {
592+
JfrGCEventSupport.emitGCPhasePauseEvent(getCollectionEpoch(), "Swap Spaces", startTicks);
593+
}
550594
} finally {
551595
counters.close();
552596
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/*
2+
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* Copyright (c) 2022, 2022, Red Hat Inc. All rights reserved.
4+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5+
*
6+
* This code is free software; you can redistribute it and/or modify it
7+
* under the terms of the GNU General Public License version 2 only, as
8+
* published by the Free Software Foundation. Oracle designates this
9+
* particular file as subject to the "Classpath" exception as provided
10+
* by Oracle in the LICENSE file that accompanied this code.
11+
*
12+
* This code is distributed in the hope that it will be useful, but WITHOUT
13+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15+
* version 2 for more details (a copy is included in the LICENSE file that
16+
* accompanied this code).
17+
*
18+
* You should have received a copy of the GNU General Public License version
19+
* 2 along with this work; if not, write to the Free Software Foundation,
20+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
21+
*
22+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
23+
* or visit www.oracle.com if you need additional information or have any
24+
* questions.
25+
*/
26+
package com.oracle.svm.core.genscavenge;
27+
28+
import com.oracle.svm.core.jfr.HasJfrSupport;
29+
import org.graalvm.nativeimage.StackValue;
30+
import org.graalvm.word.UnsignedWord;
31+
32+
import com.oracle.svm.core.annotate.Uninterruptible;
33+
import com.oracle.svm.core.jfr.JfrBuffer;
34+
import com.oracle.svm.core.jfr.JfrEvent;
35+
import com.oracle.svm.core.jfr.JfrNativeEventWriter;
36+
import com.oracle.svm.core.jfr.JfrNativeEventWriterData;
37+
import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess;
38+
import com.oracle.svm.core.jfr.JfrThreadLocal;
39+
import com.oracle.svm.core.jfr.JfrTicks;
40+
import com.oracle.svm.core.jfr.SubstrateJVM;
41+
import com.oracle.svm.core.util.VMError;
42+
43+
class JfrGCEventSupport {
44+
private static final int MAX_PHASE_LEVEL = 4;
45+
private static int currentPhase;
46+
47+
public static long startGCPhasePause() {
48+
if (!HasJfrSupport.get()) {
49+
return 0;
50+
}
51+
pushPhase();
52+
return JfrTicks.elapsedTicks();
53+
}
54+
55+
@Uninterruptible(reason = "Accesses a JFR buffer.")
56+
public static void emitGCPhasePauseEvent(UnsignedWord gcEpoch, String name, long startTicks) {
57+
if (!HasJfrSupport.get()) {
58+
return;
59+
}
60+
61+
int level = popPhase();
62+
JfrEvent event = getGCPhasePauseEvent(level);
63+
if (SubstrateJVM.isRecording() && SubstrateJVM.get().isEnabled(event)) {
64+
long end = JfrTicks.elapsedTicks();
65+
JfrBuffer buffer = ((JfrThreadLocal) SubstrateJVM.getThreadLocal()).getNativeBuffer();
66+
JfrNativeEventWriterData data = StackValue.get(JfrNativeEventWriterData.class);
67+
JfrNativeEventWriterDataAccess.initialize(data, buffer);
68+
69+
JfrNativeEventWriter.beginEventWrite(data, false);
70+
JfrNativeEventWriter.putLong(data, event.getId());
71+
JfrNativeEventWriter.putLong(data, startTicks);
72+
JfrNativeEventWriter.putLong(data, end - startTicks);
73+
JfrNativeEventWriter.putEventThread(data);
74+
JfrNativeEventWriter.putLong(data, gcEpoch.rawValue());
75+
JfrNativeEventWriter.putString(data, name);
76+
JfrNativeEventWriter.endEventWrite(data, false);
77+
}
78+
}
79+
80+
/**
81+
* GCPhasePause events are used to group GC phases into a hierarchy. They don't have any
82+
* predefined meaning as they are used in a GC-specific way. The most descriptive part is the
83+
* phase name that the GC emits as part of those JFR events.
84+
*/
85+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
86+
private static JfrEvent getGCPhasePauseEvent(int level) {
87+
switch (level) {
88+
case 0:
89+
return JfrEvent.GCPhasePauseEvent;
90+
case 1:
91+
return JfrEvent.GCPhasePauseLevel1Event;
92+
case 2:
93+
return JfrEvent.GCPhasePauseLevel2Event;
94+
case 3:
95+
return JfrEvent.GCPhasePauseLevel3Event;
96+
case 4:
97+
return JfrEvent.GCPhasePauseLevel4Event;
98+
default:
99+
throw VMError.shouldNotReachHere("GC phase pause level must be between 0 and 4.");
100+
}
101+
}
102+
103+
private static void pushPhase() {
104+
assert currentPhase < MAX_PHASE_LEVEL;
105+
currentPhase++;
106+
}
107+
108+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
109+
private static int popPhase() {
110+
assert currentPhase > 0;
111+
return --currentPhase;
112+
}
113+
}

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/JfrEnabled.java renamed to substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/jfr/HasJfrSupport.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,14 @@
2828

2929
import org.graalvm.compiler.api.replacements.Fold;
3030
import org.graalvm.nativeimage.ImageSingletons;
31+
import org.graalvm.nativeimage.Platform;
32+
import org.graalvm.nativeimage.Platforms;
3133

3234
/**
33-
* Used to include/exclude JFR feature and substitutions.
35+
* Returns {@code true} if the Native Image is built with JFR support. This does not necessarily
36+
* mean that JFR is really enabled at runtime.
3437
*/
35-
public class JfrEnabled implements BooleanSupplier {
38+
public class HasJfrSupport implements BooleanSupplier {
3639
@Override
3740
public boolean getAsBoolean() {
3841
return get();
@@ -44,6 +47,11 @@ public static boolean get() {
4447
}
4548
}
4649

50+
/**
51+
* Returns {@code true} if the HotSpot JVM is recording and emitting JFR events while doing the
52+
* Native Image build.
53+
*/
54+
@Platforms(Platform.HOSTED_ONLY.class)
4755
class JfrHostedEnabled implements BooleanSupplier {
4856
@Override
4957
public boolean getAsBoolean() {
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright (c) 2020, 2022, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package com.oracle.svm.core.jfr;
26+
27+
import org.graalvm.nativeimage.Platform;
28+
import org.graalvm.nativeimage.Platforms;
29+
30+
import com.oracle.svm.core.annotate.Uninterruptible;
31+
32+
/**
33+
* The event IDs depend on the metadata.xml and therefore vary between JDK versions.
34+
*/
35+
public enum JfrEvent {
36+
ThreadStart("jdk.ThreadStart"),
37+
ThreadEnd("jdk.ThreadEnd"),
38+
DataLoss("jdk.DataLoss"),
39+
ClassLoadingStatistics("jdk.ClassLoadingStatistics"),
40+
InitialEnvironmentVariable("jdk.InitialEnvironmentVariable"),
41+
InitialSystemProperty("jdk.InitialSystemProperty"),
42+
JavaThreadStatistics("jdk.JavaThreadStatistics"),
43+
JVMInformation("jdk.JVMInformation"),
44+
OSInformation("jdk.OSInformation"),
45+
PhysicalMemory("jdk.PhysicalMemory"),
46+
ExecutionSample("jdk.ExecutionSample"),
47+
NativeMethodSample("jdk.NativeMethodSample"),
48+
GCPhasePauseEvent("jdk.GCPhasePause"),
49+
GCPhasePauseLevel1Event("jdk.GCPhasePauseLevel1"),
50+
GCPhasePauseLevel2Event("jdk.GCPhasePauseLevel2"),
51+
GCPhasePauseLevel3Event("jdk.GCPhasePauseLevel3"),
52+
GCPhasePauseLevel4Event("jdk.GCPhasePauseLevel4");
53+
54+
private final long id;
55+
56+
@Platforms(Platform.HOSTED_ONLY.class)
57+
JfrEvent(String name) {
58+
this.id = JfrMetadataTypeLibrary.lookupPlatformEvent(name);
59+
}
60+
61+
@Uninterruptible(reason = "Called from uninterruptible code.", mayBeInlined = true)
62+
public long getId() {
63+
return id;
64+
}
65+
}

0 commit comments

Comments
 (0)