-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[GR-51307] Add GC NotificationEmitter Support #9799
base: master
Are you sure you want to change the base?
Changes from 2 commits
1923bf4
109d8b9
b54ae56
d56a758
299dcfc
cb33422
5302406
24494be
eb28fe6
4c09b4f
df2d4b2
71f75a8
09890a7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
/* | ||
* Copyright (c) 2024, 2024, Oracle and/or its affiliates. All rights reserved. | ||
* Copyright (c) 2024, 2024, Red Hat Inc. All rights reserved. | ||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | ||
* | ||
* This code is free software; you can redistribute it and/or modify it | ||
* under the terms of the GNU General Public License version 2 only, as | ||
* published by the Free Software Foundation. Oracle designates this | ||
* particular file as subject to the "Classpath" exception as provided | ||
* by Oracle in the LICENSE file that accompanied this code. | ||
* | ||
* This code is distributed in the hope that it will be useful, but WITHOUT | ||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
* version 2 for more details (a copy is included in the LICENSE file that | ||
* accompanied this code). | ||
* | ||
* You should have received a copy of the GNU General Public License version | ||
* 2 along with this work; if not, write to the Free Software Foundation, | ||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | ||
* | ||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | ||
* or visit www.oracle.com if you need additional information or have any | ||
* questions. | ||
*/ | ||
package com.oracle.svm.core.genscavenge; | ||
|
||
import com.oracle.svm.core.util.BasedOnJDKFile; | ||
|
||
import com.oracle.svm.core.SubstrateUtil; | ||
import com.oracle.svm.core.genscavenge.service.GcNotificationRequest; | ||
import com.oracle.svm.core.genscavenge.service.PoolMemoryUsage; | ||
import com.oracle.svm.core.genscavenge.service.Target_com_sun_management_GcInfo; | ||
import com.oracle.svm.core.genscavenge.service.Target_com_sun_management_internal_GcInfoBuilder; | ||
|
||
import com.sun.management.GarbageCollectionNotificationInfo; | ||
import com.sun.management.GcInfo; | ||
import com.sun.management.internal.GarbageCollectionNotifInfoCompositeData; | ||
import com.sun.management.internal.GcInfoBuilder; | ||
import sun.management.NotificationEmitterSupport; | ||
|
||
import javax.management.MBeanNotificationInfo; | ||
import javax.management.Notification; | ||
import javax.management.NotificationEmitter; | ||
import javax.management.openmbean.CompositeData; | ||
import java.lang.management.MemoryUsage; | ||
|
||
public abstract class AbstractGarbageCollectorMXBean extends NotificationEmitterSupport | ||
implements com.sun.management.GarbageCollectorMXBean, NotificationEmitter { | ||
|
||
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+14/src/hotspot/share/gc/serial/serialHeap.cpp#L451") // | ||
private static final String ACTION_MINOR = "end of minor GC"; | ||
|
||
@BasedOnJDKFile("https://github.com/openjdk/jdk/blob/jdk-24+14/src/hotspot/share/gc/serial/serialHeap.cpp#L718") // | ||
private static final String ACTION_MAJOR = "end of major GC"; | ||
|
||
private GcInfoBuilder gcInfoBuilder; | ||
private volatile GcInfo gcInfo; | ||
private long seqNumber = 0; | ||
|
||
private synchronized GcInfoBuilder getGcInfoBuilder() { | ||
if (gcInfoBuilder == null) { | ||
Target_com_sun_management_internal_GcInfoBuilder gib = new Target_com_sun_management_internal_GcInfoBuilder(this, getMemoryPoolNames()); | ||
gcInfoBuilder = SubstrateUtil.cast(gib, GcInfoBuilder.class); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can avoid this cast if you use |
||
} | ||
return gcInfoBuilder; | ||
} | ||
|
||
/** | ||
* Use the data taken from the request queue to populate MemoryUsage. The service thread calls | ||
* this method. | ||
*/ | ||
public void createNotification(GcNotificationRequest request) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Parts of this code are probably based on OpenJDK code. If so, please annotate with |
||
AbstractMemoryPoolMXBean[] beans = GenScavengeMemoryPoolMXBeans.singleton().getMXBeans(); | ||
|
||
String[] poolNames = getMemoryPoolNames(); | ||
MemoryUsage[] before = new MemoryUsage[poolNames.length]; | ||
MemoryUsage[] after = new MemoryUsage[poolNames.length]; | ||
|
||
// Pools must be in the order of getMemoryPoolNames() to match GcInfoBuilder | ||
for (int i = 0; i < poolNames.length; i++) { | ||
for (int j = 0; j < beans.length; j++) { | ||
PoolMemoryUsage pmu = request.getPoolBefore(j); | ||
if (pmu.name != null && pmu.name.equals(poolNames[i])) { | ||
before[i] = beans[j].memoryUsage(pmu.used, pmu.committed); | ||
pmu = request.getPoolAfter(j); | ||
after[i] = beans[j].memoryUsage(pmu.used, pmu.committed); | ||
} | ||
} | ||
} | ||
|
||
// Number of GC threads. | ||
Object[] extAttribute = new Object[]{Integer.valueOf(1)}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In this particular case, I would prefer something like the following, as it makes things clearer and more readable:
|
||
|
||
Target_com_sun_management_GcInfo targetGcInfo = new Target_com_sun_management_GcInfo(getGcInfoBuilder(), request.epoch, request.startTime, request.endTime, before, after, extAttribute); | ||
gcInfo = SubstrateUtil.cast(targetGcInfo, GcInfo.class); | ||
|
||
GarbageCollectionNotificationInfo info = new GarbageCollectionNotificationInfo( | ||
getName(), | ||
request.isIncremental ? ACTION_MINOR : ACTION_MAJOR, | ||
request.cause.getName(), | ||
gcInfo); | ||
|
||
CompositeData cd = GarbageCollectionNotifInfoCompositeData.toCompositeData(info); | ||
|
||
Notification notif = new Notification(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION, | ||
getObjectName(), | ||
getNextSeqNumber(), | ||
request.timestamp, | ||
getName()); | ||
notif.setUserData(cd); | ||
|
||
sendNotification(notif); | ||
} | ||
|
||
private long getNextSeqNumber() { | ||
return ++seqNumber; | ||
} | ||
|
||
@Override | ||
public MBeanNotificationInfo[] getNotificationInfo() { | ||
return new MBeanNotificationInfo[]{ | ||
new MBeanNotificationInfo( | ||
new String[]{GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION}, | ||
"javax.management.Notification", | ||
"GC Notification") | ||
}; | ||
} | ||
|
||
@Override | ||
public GcInfo getLastGcInfo() { | ||
return gcInfo; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This returns the last There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh that's a good point. And it's worse if the queue has more items in it. It looks like Hotspot always returns info from the most recent GC. I'll change this method to do the same. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've fixed the approach here by always caching the GC info during the collection. This is basically what Hotspot is doing too. |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,18 +66,28 @@ UnsignedWord getInitialValue() { | |
return initialValue; | ||
} | ||
|
||
public abstract UnsignedWord getUsedBytes(); | ||
|
||
public UnsignedWord getCommittedBytes() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
return getUsedBytes(); | ||
} | ||
|
||
abstract UnsignedWord computeInitialValue(); | ||
|
||
abstract void beforeCollection(); | ||
|
||
abstract void afterCollection(); | ||
|
||
MemoryUsage memoryUsage(UnsignedWord usedAndCommitted) { | ||
return new MemoryUsage(getInitialValue().rawValue(), usedAndCommitted.rawValue(), usedAndCommitted.rawValue(), getMaximumValue().rawValue()); | ||
return memoryUsage(usedAndCommitted, usedAndCommitted); | ||
} | ||
|
||
MemoryUsage memoryUsage(UnsignedWord used, UnsignedWord committed) { | ||
return new MemoryUsage(getInitialValue().rawValue(), used.rawValue(), committed.rawValue(), getMaximumValue().rawValue()); | ||
return memoryUsage(used.rawValue(), committed.rawValue()); | ||
} | ||
|
||
public MemoryUsage memoryUsage(long used, long committed) { | ||
return new MemoryUsage(getInitialValue().rawValue(), used, committed, getMaximumValue().rawValue()); | ||
} | ||
|
||
@Override | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,6 +66,7 @@ | |
import com.oracle.svm.core.genscavenge.HeapChunk.Header; | ||
import com.oracle.svm.core.genscavenge.UnalignedHeapChunk.UnalignedHeader; | ||
import com.oracle.svm.core.genscavenge.remset.RememberedSet; | ||
import com.oracle.svm.core.genscavenge.service.ServiceSupport; | ||
import com.oracle.svm.core.graal.RuntimeCompilation; | ||
import com.oracle.svm.core.heap.CodeReferenceMapDecoder; | ||
import com.oracle.svm.core.heap.GC; | ||
|
@@ -103,6 +104,7 @@ | |
import com.oracle.svm.core.threadlocal.VMThreadLocalSupport; | ||
import com.oracle.svm.core.util.TimeUtils; | ||
import com.oracle.svm.core.util.VMError; | ||
import com.oracle.svm.core.VMInspectionOptions; | ||
|
||
import jdk.graal.compiler.api.replacements.Fold; | ||
|
||
|
@@ -240,6 +242,9 @@ assert getCollectionEpoch().equal(data.getRequestingEpoch()) || | |
try { | ||
ThreadLocalAllocation.disableAndFlushForAllThreads(); | ||
GenScavengeMemoryPoolMXBeans.singleton().notifyBeforeCollection(); | ||
if (VMInspectionOptions.hasGcNotificationSupport()) { | ||
ServiceSupport.singleton().beforeCollection(Isolates.getCurrentUptimeMillis()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better to consistently use the values from the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok I've update this to use |
||
} | ||
HeapImpl.getAccounting().notifyBeforeCollection(); | ||
|
||
verifyHeap(Before); | ||
|
@@ -257,6 +262,10 @@ assert getCollectionEpoch().equal(data.getRequestingEpoch()) || | |
GenScavengeMemoryPoolMXBeans.singleton().notifyAfterCollection(); | ||
ChunkBasedCommittedMemoryProvider.get().afterGarbageCollection(); | ||
|
||
if (VMInspectionOptions.hasGcNotificationSupport()) { | ||
ServiceSupport.singleton().afterCollection(!isCompleteCollection(), cause, getCollectionEpoch(), Isolates.getCurrentUptimeMillis()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just to be consistent with other code in that area, please directly access the field |
||
} | ||
|
||
printGCAfter(cause); | ||
JfrGCHeapSummaryEvent.emit(JfrGCWhen.AFTER_GC); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -79,7 +79,7 @@ public void notifyAfterCollection() { | |
invalidData = false; | ||
} | ||
|
||
HeapSizes getHeapSizesBeforeGc() { | ||
public HeapSizes getHeapSizesBeforeGc() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think that this change is needed. |
||
return beforeGc; | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that this need to be
synchronized
(only called by the service thread).Please move private helper methods to a place that is below their caller (they are not super important, so they are usually not the first code that someone who opens that class wants to see).