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

Add JFR GCHeapSummary event implementation #6466

Merged
merged 9 commits into from
Jun 1, 2023
Merged

Conversation

kazukg
Copy link
Contributor

@kazukg kazukg commented Apr 21, 2023

TL;DR

This pull request allows native applications to emit jdk.GCHeapSummary which is a jfr-event.

Jdk.GCHeapSummary is a jfr-event that has not yet been implemented.
GCHeapSummary is useful for observing changes of usedHeapSize that occur before and after a garbage collection event.

The sample events

jdk.GCHeapSummary {
  startTime = 00:37:25.636
  gcId = 7
  when = "Before GC"
  heapSpace = {
    start = 0x00000000
    committedEnd = 0x00000000
    committedSize = 0 bytes
    reservedEnd = 0x00000000
    reservedSize = 0 bytes
  }
  heapUsed = 218.6 MB
}

jdk.GCHeapSummary {
  startTime = 00:37:25.639
  gcId = 7
  when = "After GC"
  heapSpace = {
    start = 0x00000000
    committedEnd = 0x00000000
    committedSize = 0 bytes
    reservedEnd = 0x00000000
    reservedSize = 0 bytes
  }
  heapUsed = 138.0 MB
}

We can visually monitor the heap usage of the application using VisualVM or JDK Mission Control.

Goals

Implement Start Time, gcId(gcEpoch), gcWhen, usedHeapSize in GCHeapSummary.

Non-Goals

Do not yet implement start, committedEnd, committedSize, reservedEnd, reservedSize in GCHeapSummary.

Because I've been struggling to find out where I can obtain those specific values.
If someone can offer suggestions on how to accomplish those tasks, I am willing to give it a shot.

Details

I developed it with reference to the implementation of jdk.GarbageCollection.

  1. Add GCHeapSummaryEvent, GCHeapSummaryEventSupport.
  2. Modified JfrEvent to add jdk.GCHeapSummary.
  3. Modified GCImpl to call GCHeapSummaryEvent.

Added JfrGCWhen to manage strings like "Before GC" and "After GC".

  1. Added JfrGCWhenSerializer and JfrGCWhens.
  2. Modified JfrFeature and JfrType to add those classes.

Test Code

  1. Add TestGCHeapSummaryEvent
  2. Modified JfrFileParser and GCWhenConstantPoolParser to test JfrType.GCWhen.

I have verified that I can display graphs using VisualVM or JDK Mission Control.

visualvm

JDKMissionControl

@oracle-contributor-agreement oracle-contributor-agreement bot added the OCA Verified All contributors have signed the Oracle Contributor Agreement. label Apr 21, 2023
@kazukg kazukg changed the title Add GCHeapSummaryEvent Add JFR GCHeapSummary event implementation Apr 21, 2023
@fniephaus
Copy link
Member

Could you take a look at this please, @roberttoyonaga?

Copy link
Collaborator

@roberttoyonaga roberttoyonaga left a comment

Choose a reason for hiding this comment

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

Hi @kazukg! Thanks for contributing. I've added a few minor comments.


public static void emitJfrGCHeapSummaryEventBeforeGC(UnsignedWord gcEpoch,long start,long heapUsed) {
if (hasJfrSupport() ) {
jfrSupport().emitGCHeapSummaryEventBeforeGC(gcEpoch, start,heapUsed);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can you run the eclipse formatter please? As well as mx checkstyle. I think that could help fix some of the PR gate issues.

Copy link
Contributor Author

@kazukg kazukg Apr 25, 2023

Choose a reason for hiding this comment

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

Hi @roberttoyonaga !
Thank you for confirming. I'll do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I made changes to the source code and ran mx checkstyle on the substratevm folder, verifying that there are no errors.

}

@Uninterruptible(reason = "Accesses a JFR buffer.")
public void emitGCHeapSummaryEventAfterGC(UnsignedWord gcEpoch, long start, long heapUsed) {
Copy link
Collaborator

@roberttoyonaga roberttoyonaga Apr 24, 2023

Choose a reason for hiding this comment

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

If emitGCHeapSummaryEventAfterGC(UnsignedWord, long, long) and emitGCHeapSummaryEventBeforeGC(UnsignedWord, long, long) are basically the same, why not just add another method emitGCHeapSummaryEvent(UnsignedWord, long , long, JfrGCWhen) to reduce duplication?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@roberttoyonaga
Could you review the changes I made to the source code?

public void beforeAnalysis(BeforeAnalysisAccess access) {
if (HasJfrSupport.get()) {

//ImageSingletons.add(JfrGCWhens.class, new JfrGCWhens());
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think you can remove this comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed it.



@AutomaticallyRegisteredFeature
class JfrGCHeapSummaryEventFeature implements InternalFeature {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm I think this should actually be similar to JfrGCEventFeature and only be included if the default SerialGC is being used. See isInConfiguration

Copy link
Contributor Author

@kazukg kazukg May 1, 2023

Choose a reason for hiding this comment

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

I refered to the source code of JfrGCEventFeature, and added isInConfiguration.

int id = whens.length;
JfrGCWhen result = new JfrGCWhen(id, when);

JfrGCWhen[] newArr = Arrays.copyOf(whens, id + 1);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Instead of having the logic this method provides, is there any reason you can't just hardcode initialize the array with "Before GC" and "After GC" JfrGCWhens? Is there a possibility of needing to add other JfrGCWhens in the future?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't believe that we will need to add another JfrGCWhen in the future.
I would modify the code to hardcode.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the source code to hardcode the initialization of JfrGCWhens.whens, and added getBeforeGCWhen and getAfterGCWhen methods to allow JfrGCHeapSummaryEventSupport to obtain their values.

- Added `emitGCHeapSummaryEvent` method to reduce duplication.
- Hardcoded initialization of `JfrGCWhens.whens`.
Added isInConfiguration to include JfrGCHeapSummaryEventFeature only when the default SerialGC is used.
Copy link
Collaborator

@roberttoyonaga roberttoyonaga 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 making those changes. I added a couple more minor comments.

@@ -44,6 +44,8 @@ public enum JfrType {
FrameType("jdk.types.FrameType"),
GCCause("jdk.types.GCCause"),
GCName("jdk.types.GCName"),
GCWhen("jdk.types.GCWhen"),
VirtualSpace("jdk.types.VirtualSpace"),
Copy link
Collaborator

Choose a reason for hiding this comment

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

Is this used anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Indeed, it does seem unnecessary. Initially, I added it without fully understanding it. I will remove it and verify that it does not affect the functionality.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I removed Definition of Virtual Space from JrfType.


class JfrGCHeapSummaryEventSupport {

private final JfrGCWhen before;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Would it be simpler if you didn't cache these JfrGCWhens and instead just called JfrGCWhens.singleton().getBeforeGCWhen() and JfrGCWhens.singleton().getAfterGCWhen() from emitGCHeapSummaryEventBeforeGC and emitGCHeapSummaryEventAfterGC respectively?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have completed it.

// VirtualSpace
JfrNativeEventWriter.putLong(data, 0L); // start
JfrNativeEventWriter.putLong(data, 0L); // committedEnd : ulong
JfrNativeEventWriter.putLong(data, 0L); // committedSize : ulong
Copy link
Collaborator

Choose a reason for hiding this comment

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

Just some thoughts: You could try using GCImpl.getPolicy().getCurrentHeapCapacity() to record the committedSize. I'm not certain, but reservedSize might not be applicable here, so maybe it makes the most sense to leave reservedEnd and reservedSize as 0 for now anyway. You listed these as non-goals so perhaps they can be done at a later time instead.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for the advice. I am not currently able to try it out but I will do so in a few days based on your recommendation.

Copy link
Contributor Author

@kazukg kazukg May 10, 2023

Choose a reason for hiding this comment

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

I have added code to output the committed size, and I have verified that the committed size is recorded in jdk.GCHeapSummary.

jdk.GCHeapSummary {
  startTime = 07:17:28.397
  gcId = 0
  when = "Before GC"
  heapSpace = {
    start = 0x00000000
    committedEnd = 0x00000000
    committedSize = 120.0 MB
    reservedEnd = 0x00000000
    reservedSize = 0 bytes
  }
  heapUsed = 48.1 MB
}
jdk.GCHeapSummary {
  startTime = 07:17:28.398
  gcId = 0
  when = "After GC"
  heapSpace = {
    start = 0x00000000
    committedEnd = 0x00000000
    committedSize = 105.0 MB
    reservedEnd = 0x00000000
    reservedSize = 0 bytes
  }
  heapUsed = 599.8 kB
}

- Changed not to cache
- Improved code to emit committed Size.
@roberttoyonaga
Copy link
Collaborator

Thank you for making those changes. This seems good to me now. @christianhaeubl when you have time, can you have a look at this as well to see if it's ready for integration?


class JfrGCHeapSummaryEventSupport {

public void emitGCHeapSummaryEventBeforeGC(UnsignedWord gcEpoch, long start, long committedSize, long heapUsed) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: As a refactoring, you could get rid of emitGCHeapSummaryEventBeforeGC and emitGCHeapSummaryEventAfterGC and call emitGCHeapSummaryEvent(UnsignedWord , long , long , long , JfrGCWhen) directly from emitJfrGCHeapSummaryEventBeforeGC and emitJfrGCHeapSummaryEventAfterGC (by supplying JfrGCWhens.singleton().getBeforeGCWhen() or JfrGCWhens.singleton().getAfterGCWhen().)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll do that.


public class JfrGCWhens {

private JfrGCWhen[] whens = new JfrGCWhen[]{
Copy link
Collaborator

Choose a reason for hiding this comment

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

nit: I think this can be final

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 few comments.

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2022, 2022, Oracle and/or its affiliates. All rights reserved.
Copy link
Member

Choose a reason for hiding this comment

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

Please adjust the copyright headers of the files that you added so that they contain the correct year (it seems that you copy/pasted them from other files).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@christianhaeubl

Thank you for pointing that out.
I was copying and pasting the headers from other files.

I have made some modifications to several files, in addition to the ones I added. Do I need to update the copyright headers of those files as well?

Specifically, the files in question are JfrEvent, JfrFeature, JftType, and JftFileParser.

Copy link
Member

Choose a reason for hiding this comment

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

Regarding the existing files that you modified: you can update the copyright headers if you want to but you don't need to.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for reply.
I only modify the copyright headers of the files that I've added.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have completed it.


import com.oracle.svm.core.Uninterruptible;

public class JfrGCWhen {
Copy link
Member

Choose a reason for hiding this comment

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

Please use an enum instead of a class. Then, you also don't need the class JfrGCWhens.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll do that.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have completed it.

import com.oracle.svm.core.jfr.JfrNativeEventWriterData;
import com.oracle.svm.core.jfr.JfrNativeEventWriterDataAccess;

class JfrGCHeapSummaryEventSupport {
Copy link
Member

Choose a reason for hiding this comment

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

This class is stateless, so all its methods can be static. After doing that change, you can merge this class with JfrGCHeapSummaryEvent and you can remove the class JfrGCHeapSummaryEventFeature. In the end, your code should look similar to the class ThreadParkEvent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for providing a specific example.
I will refer to the implementation of ThreadParkEvent.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have modified JfrGCHeapSummaryEvent to be composed solely of static methods.

- Changed to be processed by static method
- Replaced class with an enum for improved
- Updated copyright headers to ensure accuracy
checksyle error
@christianhaeubl
Copy link
Member

Thanks, I did further cleanups on top, see #6705. I also changed how the used/committed heap size is queried so that the logic is more consistent with other places where we are already querying that information. Let me know if you see any issues with those changes. This PR should get merged in the next few days.

@graalvmbot graalvmbot merged commit a9b50f9 into oracle:master Jun 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
OCA Verified All contributors have signed the Oracle Contributor Agreement.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants