Skip to content
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

Open
wants to merge 13 commits into
base: master
Choose a base branch
from

Conversation

roberttoyonaga
Copy link
Collaborator

@roberttoyonaga roberttoyonaga commented Oct 1, 2024

Summary

This PR adds support for the emission of GC notifications from GC MXBeans. Previously, SubstratVM's GC MXBeans (CompleteGarbageCollectorMXBean and IncrementalGarbageCollectorMXBean) implemented NotificationEmitter, but actually did not support notifications.

Details

A new feature ServiceFeature has been added. Use --enable-monitoring=gcnotif to include the feature at build time and add support for GC notifications.

The ImageSingleton ServiceSupport starts up a dedicated service thread which asynchronously handles creating and sending notifications. Notification "requests" are created during GC safepoints. They temporarily hold the information later needed to create and send GC notifications. During the GC safepoint, the thread executing the VM operation enqueues a request in GcNotifier and signals the service thread that a request is ready. That process should be relatively quick and allocation free. Once the safepoint is over, the service thread uses the queued requests to create notifications that are sent by the appropriate GC MXBean.

The new classes GcNotifier and ServiceThread are meant to serve almost the same purposes as the GCNotifier and ServiceThread classes in Hotspot respectively.

The various GC MXBeans now extend AbstractGarbageCollectorMXBean which contains the code for creating and sending notifications from request data. This class serves a similar purpose to com.sun.management.internal.GarbageCollectorExtImpl. Additionally, this class extends sun.management.NotificationEmitterSupport from which the listener registration/deregistration logic is reused.

Other notes

I'm not sure about the naming of ServiceSupport and ServiceFeature. Maybe NotificationSupport makes more sense? I decided on the "service" prefix because, in Hotspot, the GC notification code is in the /hotspot/share/services directory. Also, maybe we will want to add other services in the future.

Related Issues: #8237

@oracle-contributor-agreement oracle-contributor-agreement bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label Oct 1, 2024
@christianhaeubl christianhaeubl changed the title Add GC NotificationEmitter Support [GR-51307] Add GC NotificationEmitter Support Oct 10, 2024
Copy link
Member

@christianhaeubl christianhaeubl left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR, I added a larger number of comments.

private volatile GcInfo gcInfo;
private long seqNumber = 0;

private synchronized GcInfoBuilder getGcInfoBuilder() {
Copy link
Member

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).

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);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can avoid this cast if you use Target_com_sun_management_internal_GcInfoBuilder instead of GcInfoBuilder everywhere (including in the constructor of Target_com_sun_management_GcInfo).


@Override
public GcInfo getLastGcInfo() {
return gcInfo;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This returns the last gcInfo that was processed by the service thread (i.e., new information will be available with a delay). Is the behavior the same on HotSpot or do they always report up-to-date data?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The 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.

}

// Number of GC threads.
Object[] extAttribute = new Object[]{Integer.valueOf(1)};
Copy link
Member

Choose a reason for hiding this comment

The 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:

        Object[] extAttribute = new Object[1];
        extAttribute[0] = 1; // number of GC threads

import org.graalvm.word.UnsignedWord;

public class ServiceSupport {
private GcNotifier gcNotifier;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This field and serviceThread can be final.

* We don't need to drain all requests since signals queue at the semaphore level. The rest
* of the requests will be handled in subsequent calls to this method by the sevice thread.
*/
if (!updateCurrentRequest()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please adapt this logic once you switch to VMMutex and VMCondition.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now handled by checking NotificationThread.hasRequests() before blocking in the notification thread loop.

* This class is the dedicated thread that handles services. For now, the only service is GC
* notifications.
*/
public class ServiceThread extends Thread {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does HotSpot use the service thread or a separate notification thread for the GC notifications?

Copy link
Collaborator Author

@roberttoyonaga roberttoyonaga Oct 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hotspot uses NotificationThread, which is different than ServiceThread. Should I rename this class "NotificationThread" instead? Unless we plan to have a single dedicated thread handle service and notifications.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Renamed everything to notification*


@Platforms(Platform.HOSTED_ONLY.class)
@SuppressWarnings("this-escape")
public ServiceThread(GcNotifier gcNotifier) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please set a name for this thread, and potentially also a thread group (please check in the HotSpot code).

* Use the data taken from the request queue to populate MemoryUsage. The service thread calls
* this method.
*/
public void createNotification(GcNotificationRequest request) {
Copy link
Member

Choose a reason for hiding this comment

The 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 @BasedOnJDKFile.

* questions.
*/

package com.oracle.svm.core.genscavenge.service;
Copy link
Member

@christianhaeubl christianhaeubl Oct 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move everything in this package to a sub package in the GC independent project com.oracle.svm.core.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried doing that originally, but it seems I cannot import from com.oracle.svm.core.genscavenge from inside com.oracle.svm.core. Is there a way to work around this?

The build error:
error: package com.oracle.svm.core.genscavenge does not exist

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will need to split the implementation into a GC independent and a GC-specific part. This will also make it easier to implement this for all Native Image GCs in the future:

  • GC independent parts in com.oracle.svm.core: notification thread, notification logic, and all the GC independent logic that accesses the memory pools. You will probably need introduce an interface and implementation that interface on the GC-specific side.
  • GC specific parts in com.oracle.svm.core.genscavenge: essentially everything that would be different for a completely different GC implementation (e.g., the logic in GenScavengeMemoryPoolMXBeans, how many memory pools a GC uses, or how the memory pools are named)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I put the files into com.oracle.svm.core.notification and com.oracle.svm.core.gc (the naming could maybe be improved).

com.oracle.svm.core.gc contains the interface MemoryPoolMXBeansProvider for accessing GC-specific memory pool info.

I also put AbstractGarbageCollectorMXBean inside com.oracle.svm.core.gc. Different GCs can override methods like gcThreadCount() and isIncremental() so that the notification infrastructure knows how to handle notifications for different collectors.

@roberttoyonaga
Copy link
Collaborator Author

Thank you for the review @christianhaeubl. I think I've addressed all your comments. Please have another look when you have time. Thanks!

@christianhaeubl
Copy link
Member

Thanks. I started integrating this PR. I will let you know if I run into any major issues.

@roberttoyonaga
Copy link
Collaborator Author

Thanks @christianhaeubl !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
native-image OCA Verified All contributors have signed the Oracle Contributor Agreement. redhat-interest
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants