垃圾收集通知?

时间:2010-01-13 15:26:44

标签: java garbage-collection

我想在JVM上注册一个回调,所以我知道垃圾收集何时发生。有没有办法做到这一点?

编辑:我想这样做,所以当我的应用程序日志中发生垃圾收集时我可以注销,所以我可以看到它是否与我看到的问题有关。打开-Xloggc很有帮助,但是将GC日志(自应用程序启动后使用秒数)的时间集成到我的主应用程序日志中有点棘手。

编辑2012年4月:从Java7u4开始,您可以从GarbageCollectorMXBean(一个不错的example)获取通知。

11 个答案:

答案 0 :(得分:18)

答案 1 :(得分:10)

我认为标准方法是使用JVM Tool Interface (JVM TI)编写具有GC启动回调的代理并记录时间(参见GetTime)。请注意,仅为完整的GC发送Garbage Collection Start event

示例JVM TI代理程序位于JDK 5.0或JDK 6下载的演示目录中。技术文章The JVM Tool Interface (JVM TI): How VM Agents Work是另一个非常好的资源。另请查看Creating a Debugging and Profiling Agent with JVMTI

答案 2 :(得分:7)

使用接受的答案中提到的GarbageCollectorMXBean

Java代码示例

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.

答案 3 :(得分:3)

看起来你可以使用MemoryPoolMXBean并将集合使用阈值设置为1.这应该在gc运行时给你一个通知,并且仍然至少有一个字节的内存在使用。

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

但看起来这并不适用于所有垃圾收集器。

答案 4 :(得分:2)

当收到垃圾收集的JVMTI事件时,JVM在技术上已停止,因此它无法通过JNI回调Java监听器....此代理打印出GC开始和结束时的时间,其分辨率高于详细GC在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;
}

答案 5 :(得分:2)

获取即将发生的GC通知的另一个用例:如果您的应用程序是负载平衡的,那么您可以通知负载均衡器在GC即将开始时将您的节点从池中取出它不会收到请求必须等待完整的GC才能得到处理。

这对于在GC启动之前到达的飞行请求没有帮助,但至少在我的情况下,大多数请求是亚秒级的,主要的GC是每隔几分钟5-10秒。我们可以调整NewGen比率等,但一般情况仍然适用:主要GC可能比典型响应时间长得多,因此您可能希望 premptively 停止启动主要GC的节点接收请求。

当GC结束时,JVM中的一个线程可以向负载均衡器发送通知,让它知道它的业务回报,或者LB可以依赖其通常的keepalive。

答案 6 :(得分:2)

我知道这已经很晚了,但我希望有一天它会对某人有所帮助。

您可以使用我正在开发的名为gcRadar的库来接收此类事件。它提供有关对象何时被垃圾收集的确切信息。

欢迎任何有关图书馆改进的建议。

答案 7 :(得分:1)

interesting article on Javalobby讨论了这样做的一种方法。

答案 8 :(得分:1)

您自己的程序没有标准的方法从JVM获取有关垃圾收集的信息。任何此类API都是特定于供应商的。

为什么您找到的设施不足?

答案 9 :(得分:1)

关于-Xloggc:从jdk1.6 update 4开始,您可以使用-XX:+PrintGCDateStamps让Sun / Oracle JVM打印出日期和时间。这使得日志更多更有用,特别是如果您添加可以通知您任何GC问题的日志扫描程序/监视器。

答案 10 :(得分:0)

如果您将此视为诊断工具,我建议您将应用程序日志重定向到StdOut,然后将StdOut和StdErr重定向到文件中。这将为您提供JVM日志记录的详细信息,而无需强制您更改应用程序代码。