Java 垃圾收集通知?

Java 垃圾收集通知?,java,garbage-collection,Java,Garbage Collection,我想在JVM中注册一个回调,以便知道垃圾回收何时发生。有没有办法做到这一点 编辑:我想这样做,以便在应用程序日志中发生垃圾收集时可以注销,以便查看它是否与我看到的问题相关。启用-Xloggc是有帮助的,但是要将GC日志中的时间(从应用程序启动开始使用秒数)集成到我的主应用程序日志中有点棘手 2012年4月编辑:从Java7u4开始,您可以从GarbageCollectorMXBean(一个nice)获得通知。有一种方法可以做到这一点。看起来您可以使用MemoryPoolMXBean并将收集使用阈

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

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


2012年4月编辑:从Java7u4开始,您可以从GarbageCollectorMXBean(一个nice)获得通知。

有一种方法可以做到这一点。

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


不过,这似乎并不适用于所有垃圾收集器。

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


为什么您发现设施不足

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

我认为标准的方法是使用编写带有GC启动回调的代理,并记录从它开始的时间(请参阅)。请注意,仅针对完整地面军事系统发送


jdk5.0或jdk6下载的demo目录中提供了示例jvmti代理。科技文章是另一个很好的资源。也来看看

当接收到用于垃圾收集的JVMTI事件时,JVM在技术上已停止,因此无法通过JNI回调Java侦听器。。。。此代理以比Sun JVM上的详细GC更高的分辨率打印GC开始和结束时的时间

#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;
}
#包括
#包括
#包括
#包括“jvmti.h”
无效打印时间(常量字符*类型){
结构时间值电视;
gettimeofday(&tv,NULL);
struct tm localTime;
localtime\r(&tv.tv\u秒,&localtime);
char*startTime=calloc(1128);
strftime(开始时间,(大小)128,“%a%b%d%Y%H:%M:%S”,&localTime);
fprintf(stderr,“GC%s:%s.%06d\n”,类型,开始时间,(int)tv.tv\u usec);
fflush(stderr);
如果(开始时间)空闲(开始时间);
}
无效JNICALL
垃圾收集启动(jvmtiEnv*jvmti_环境){
打印时间(“开始”);
}
无效JNICALL
垃圾收集整理(jvmtiEnv*jvmti_环境){
打印时间(“完成”);
}
JNIEXPORT jint JNICALL
代理加载(JavaVM*jvm,char*选项,void*保留)
{
jvmtiEnv*jvmti_环境;
jint returnCode=(*jvm)->GetEnv(jvm,(void**)和jvmti_env,
JVMTI_版本(1_0);
如果(返回代码!=JNI_OK)
{
fprintf(标准,
“此JVM不支持请求的JVMTI版本(1.0)。\n”);
返回JVMTI\u错误\u不支持的\u版本;
}
JVM能力*所需能力;
requiredCapabilities=(JVMTICAabilities*)calloc(1,sizeof(JVMTICAabilities));
如果(!requiredCapabilities)
{
fprintf(stderr,“无法分配内存”);
返回JVMTI_ERROR_OUT_OF_内存;
}
requiredCapabilities->can_generate_garbage_collection_events=1;
如果(返回代码!=JNI_OK)
{
fprintf(stderr,“C:\tJVM没有所需的功能(%d)\n”,
返回代码);
出口(-1);
}
returnCode=(*jvmti_env)->AddCapabilities(jvmti_env,requiredCapabilities);
jvmtiEventCallbacks*事件回调;
eventCallbacks=calloc(1,sizeof(jvmtiEventCallbacks));
如果(!eventCallbacks)
{
fprintf(stderr,“无法分配内存”);
返回JVMTI_ERROR_OUT_OF_内存;
}
eventCallbacks->GarbageCollectionStart=&GarbageCollectionStart;
eventCallbacks->GarbageCollectionFinish=&GarbageCollectionFinish;
returnCode=(*jvmti_env)->SetEventCallbacks(jvmti_env,
eventCallbacks,(jint)sizeof(*eventCallbacks));
如果(返回代码!=JNI_OK)
{
fprintf(stderr,“C:\tError设置事件回调(%d)\n”,
返回代码);
出口(-1);
}
returnCode=(*jvmti_env)->SetEventNotificationMode(
jvmti_env、jvmti_ENABLE、jvmti_EVENT_GARBAGE_COLLECTION_START、(jthread)NULL);
如果(返回代码!=JNI_OK)
{
fprintf(
斯特德尔,
“C:\tJVM没有所需的功能、JVMTI\u启用、JVMTI\u事件\u垃圾收集\u启动(%d)\n”,
返回代码);
出口(-1);
}
returnCode=(*jvmti_env)->SetEventNotificationMode(
jvmti_env、jvmti_ENABLE、jvmti_EVENT_GARBAGE_COLLECTION_FINISH(jthread)NULL);
如果(返回代码!=JNI_OK)
{
fprintf(
斯特德尔,
“C:\tJVM没有所需的功能、JVMTI\u启用、JVMTI\u事件\u垃圾收集\u完成(%d)\n”,
返回代码);
出口(-1);
}
如果(要求的能力)免费(要求的能力);
if(eventCallbacks)free(eventCallbacks);
返回JVMTI\u错误\u无;
}

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


获取即将到来的GC通知的另一个用例:如果你的应用程序是负载平衡的,那么你可以在GC即将开始时通知负载平衡器将你的节点从池中取出,它不会接收到需要等待完整GC处理的请求

这对GC启动前到达的飞行中请求没有帮助,但至少在我的情况下,大多数请求是亚秒级的,主要的GC是5-10秒,每几分钟一次。我们可以调整NewGen比率等,但一般的观点仍然适用:主要GC可能比
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);
    }
}