39

I'd like to register a callback with the JVM so I know when garbage collection is happening. Is there any way to do this?

EDIT: I want to do this so I can log out when garbage collection happens in my application log, so I can see if it correlates to problems I'm seeing. Turning on -Xloggc is helpful, but it is a little tricky to integrate the times from the GC log (which use seconds since app start) into my main application log.

EDIT April 2012: As of Java7u4, you can get notifications from the GarbageCollectorMXBean (a nice example).

Martin Serrano
  • 3,727
  • 1
  • 35
  • 48
Ted Graham
  • 3,511
  • 4
  • 24
  • 25
  • Ideally, a developer shouldn't have to care when GC happens. You can do some setting in the JVM arguments. Just out of curiosity, what are you trying to do? Maybe there is a better event to register with. – Jay Jan 13 '10 at 15:32
  • 16
    @Jay, we don't live in an ideal world. If you really care about your service working, you probably want to alarm when garbage collection starts happening too often as it can be a sign of issues. – tster Dec 30 '11 at 18:20
  • 1
    Nice question. It would nice if GC notification could be integrated with a load-balancer to avoid sending traffic to the instance during GC. – Alex R Oct 08 '15 at 19:32
  • @AlexR Or [the other way round](https://stackoverflow.com/q/36962630/581205): Periodically select a server, let no more requests be send to it, let it finish its running requests, let it do its GC, and re-activate the server. – maaartinus Dec 22 '17 at 04:52

11 Answers11

20

As of Java7u4, you can get notifications from the GarbageCollectorMXBean. See http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html

Ted Graham
  • 3,511
  • 4
  • 24
  • 25
  • 1
    It's worth mentioning that it's often faster to watch the MemoryPoolMXBean and see when memory usage drops. – Xorlev Apr 17 '15 at 16:47
  • While MemoryPoolMXBean is much faster, you simply don't know when the GC gets run. If it's important for you to know things like the maximum memory usage of your application, then you'll want to check memory usage immediately before and after garbage collection. If you poll, you won't know for certain. Just my $0.02 :) – CodeBlind Nov 10 '16 at 17:43
10

I think that the standard way is to use the JVM Tool Interface (JVM TI) to write an agent with a GC start callback and to log the time from it (see GetTime). Note that a Garbage Collection Start event is sent for full GCs only.

Sample JVM TI agents is available in the demo directory of the JDK 5.0 or the JDK 6 download. The tech article The JVM Tool Interface (JVM TI): How VM Agents Work is another very good resource. Also have a look at Creating a Debugging and Profiling Agent with JVMTI.

Simeon Leyzerzon
  • 18,658
  • 9
  • 54
  • 82
Pascal Thivent
  • 562,542
  • 136
  • 1,062
  • 1,124
  • Good suggestion; I hadn't heard of JVM TI. However, our application almost never does a full gc, so I want to know about the gen 1 collections. What do you think of the MemoryPoolMXBean idea? – Ted Graham Jan 13 '10 at 22:24
7

Java code sample using the GarbageCollectorMXBean referred to in the accepted answer:

static
{
    // notification listener. is notified whenever a gc finishes.
    NotificationListener notificationListener = new NotificationListener()
    {
        @Override
        public void handleNotification(Notification notification,Object handback)
        {
            if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION))
            {
                // extract garbage collection information from notification.
                GarbageCollectionNotificationInfo gcInfo = GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());

                // access garbage collection information...
            }
        }
    };

    // register our listener with all gc beans
    for (GarbageCollectorMXBean gcBean : ManagementFactory.getGarbageCollectorMXBeans())
    {
        NotificationEmitter emitter = (NotificationEmitter) gcBean;
        emitter.addNotificationListener(notificationListener,null,null);
    }
}

site that has detailed sample code that uses the GarbageCollectorMXBean.

Eric
  • 16,397
  • 8
  • 68
  • 76
3

It looks like you could use the MemoryPoolMXBean and set the collection usage threshold to 1. This should give you a notification any time the gc runs and there is still at least a byte of memory in use.

http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/MemoryPoolMXBean.html

It looks like this doesn't work with all garbage collectors, though.

Adam Goode
  • 7,380
  • 3
  • 29
  • 33
2

I know this is very late but I hope it might help someone someday.

You can receive such events this by using the using the library that i am developing called gcRadar. It provides information about exactly when an object was garbage collected.

Any suggestions for improvements in the library are welcome.

R.daneel.olivaw
  • 2,681
  • 21
  • 31
2

When receiving the JVMTI event for Garbage Collection the JVM is technically stopped so it's unable to call back a Java listener via JNI.... this agent prints out the time at GC start and finish with a higher resolution than the verbose GC on the Sun JVM.

#include <stdlib.h>
#include <time.h>
#include <sys/time.h>
#include "jvmti.h"

void printGCTime(const char* type) {

  struct timeval tv;
  gettimeofday(&tv, NULL);

  struct tm localTime;
  localtime_r(&tv.tv_sec, &localTime);

  char *startTime = calloc(1, 128);

  strftime(startTime, (size_t) 128, "%a %b %d %Y %H:%M:%S", &localTime);

  fprintf(stderr, "GC %s: %s.%06d\n", type, startTime, (int)tv.tv_usec );
  fflush(stderr);

  if(startTime) free(startTime);

}

void JNICALL
garbageCollectionStart(jvmtiEnv *jvmti_env) {

  printGCTime("Start ");

}

void JNICALL
garbageCollectionFinish(jvmtiEnv *jvmti_env) {

  printGCTime("Finish");

}


JNIEXPORT jint JNICALL
Agent_OnLoad(JavaVM * jvm, char *options, void *reserved)
{
  jvmtiEnv *jvmti_env;

  jint returnCode = (*jvm)->GetEnv(jvm, (void **) &jvmti_env,
      JVMTI_VERSION_1_0);



  if (returnCode != JNI_OK)
    {
      fprintf(stderr,
          "The version of JVMTI requested (1.0) is not supported by this JVM.\n");
      return JVMTI_ERROR_UNSUPPORTED_VERSION;
    }


  jvmtiCapabilities *requiredCapabilities;

  requiredCapabilities = (jvmtiCapabilities*) calloc(1, sizeof(jvmtiCapabilities));
  if (!requiredCapabilities)
      {
        fprintf(stderr, "Unable to allocate memory\n");
        return JVMTI_ERROR_OUT_OF_MEMORY;
      }

  requiredCapabilities->can_generate_garbage_collection_events = 1;

  if (returnCode != JNI_OK)
    {
      fprintf(stderr, "C:\tJVM does not have the required capabilities (%d)\n",
          returnCode);
      exit(-1);
    }



  returnCode = (*jvmti_env)->AddCapabilities(jvmti_env, requiredCapabilities);


  jvmtiEventCallbacks *eventCallbacks;

  eventCallbacks = calloc(1, sizeof(jvmtiEventCallbacks));
  if (!eventCallbacks)
    {
      fprintf(stderr, "Unable to allocate memory\n");
      return JVMTI_ERROR_OUT_OF_MEMORY;
    }

  eventCallbacks->GarbageCollectionStart = &garbageCollectionStart;
  eventCallbacks->GarbageCollectionFinish = &garbageCollectionFinish;


  returnCode = (*jvmti_env)->SetEventCallbacks(jvmti_env,
      eventCallbacks, (jint) sizeof(*eventCallbacks));


  if (returnCode != JNI_OK)
    {
      fprintf(stderr, "C:\tError setting event callbacks (%d)\n",
          returnCode);
      exit(-1);
    }

  returnCode = (*jvmti_env)->SetEventNotificationMode(
      jvmti_env, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_START, (jthread) NULL);

  if (returnCode != JNI_OK)
    {
      fprintf(
          stderr,
          "C:\tJVM does not have the required capabilities, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_START (%d)\n",
          returnCode);
      exit(-1);
    }


  returnCode = (*jvmti_env)->SetEventNotificationMode(
      jvmti_env, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH, (jthread) NULL);

  if (returnCode != JNI_OK)
    {
      fprintf(
          stderr,
          "C:\tJVM does not have the required capabilities, JVMTI_ENABLE, JVMTI_EVENT_GARBAGE_COLLECTION_FINISH (%d)\n",
          returnCode);
      exit(-1);
    }


  if(requiredCapabilities) free(requiredCapabilities);
  if(eventCallbacks) free(eventCallbacks);

  return JVMTI_ERROR_NONE;
}
2

Another use case for getting impending GC notifications: if your app is load balanced then you can then notify the load balancer to take your node out of the pool when a GC is about to start to that it does not receive requests that would have to wait for a full GC to get dealt with.

That doesn't help in-flight requests that arrived just before the GC kicked in but, in my case at least, most requests are subsecond and major GCs are 5-10s, every few minutes. We can tweak the NewGen ratios etc but the general point still applies: major GC may be much longer than typical response times so you may want to premptively stop a node starting a major GC from receiving requests.

When GC is over, a thread in the JVM can send a notification to the load balancer to let it know its back in business or the LB can rely on its usual keepalive.

Alexis Pigeon
  • 7,423
  • 11
  • 39
  • 44
user533933
  • 39
  • 3
1

There's an interesting article on Javalobby discussing one method of doing this.

Kaleb Brasee
  • 51,193
  • 8
  • 108
  • 113
  • Interesting approach, but it introduces some overhead and only notifies you when a specific object is collected. I want a notification when GC happens, at the same frequency that the -Xloggc command logs to a file. – Ted Graham Jan 13 '10 at 15:31
1

There is no standard way for your own program to get information from the JVM about garbage collection. Any such API is vendor specific.

Why is the facility you found insufficient?

Thorbjørn Ravn Andersen
  • 73,784
  • 33
  • 194
  • 347
  • The JVM TI is standard for Java 5+ if I recall correctly. Would you like a program to listen to *itself* with that? – Thorbjørn Ravn Andersen Jan 13 '10 at 21:23
  • I can't find the ultimate link but the JVM TI seems to be implemented by most JVMs: Sun, IBM (http://publib.boulder.ibm.com/infocenter/javasdk/v6r0/index.jsp?topic=/com.ibm.java.doc.diagnostics.60/diag/tools/jvmti.html), Oracle/BEA, Harmony (and maybe more). – Pascal Thivent Jan 13 '10 at 21:25
  • Oh, I see what you meant. But an agent can be your own program :) – Pascal Thivent Jan 13 '10 at 21:26
  • Agents do not know of garbage collections. Let me suggest you actually TRY your suggestion of having a program listening to gc's in itself with JVM TI. You might be surprised :) – Thorbjørn Ravn Andersen Jan 14 '10 at 10:12
1

Regarding -Xloggc: As of jdk1.6 update 4 you can get the Sun/Oracle JVM to print out the date and time using -XX:+PrintGCDateStamps. This makes the logs much more useful, especially if you add a log scanner / monitor that can notify you of any GC issues.

Joshua Davis
  • 3,499
  • 1
  • 26
  • 29
0

If you're looking at this as a diagnostic tool, I recommend redirecting your application log to StdOut and then redirecting both StdOut and StdErr into a file. This will give you the detail of JVM logging, without forcing you to change your application code.

kdgregory
  • 38,754
  • 10
  • 77
  • 102
  • As I wrote this, I started to wonder if maybe the JVM logging could be redirected into your application logging framework. What are you using for an application logger? – kdgregory Jan 13 '10 at 15:59