我想在JVM中注册一个回调函数,以便我知道何时发生垃圾回收。有没有办法做到这一点?
编辑:我想这样做是为了在我的应用程序日志中记录垃圾回收发生的时间,以便我可以查看它是否与我所看到的问题相关。打开-Xloggc很有帮助,但将GC日志中的时间(使用应用程序启动后的秒数)集成到我的主应用程序日志中有点棘手。
编辑于2012年4月:自Java7u4以来,您可以从GarbageCollectorMXBean(一个不错的示例)获取通知。
我想在JVM中注册一个回调函数,以便我知道何时发生垃圾回收。有没有办法做到这一点?
编辑:我想这样做是为了在我的应用程序日志中记录垃圾回收发生的时间,以便我可以查看它是否与我所看到的问题相关。打开-Xloggc很有帮助,但将GC日志中的时间(使用应用程序启动后的秒数)集成到我的主应用程序日志中有点棘手。
编辑于2012年4月:自Java7u4以来,您可以从GarbageCollectorMXBean(一个不错的示例)获取通知。
从Java7u4开始,您可以从GarbageCollectorMXBean获取通知。请参见http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html
我认为标准的方法是使用JVM工具接口(JVM TI)编写一个带有GC启动回调的代理并记录从中获得的时间(参见GetTime)。请注意,只有在进行 full GCs 时才会发送垃圾回收开始事件。
JDK 5.0或JDK 6下载的演示目录中提供了示例JVM TI代理。技术文章“JVM工具接口(JVM TI):VM代理的工作原理”也是另一个非常好的资源。还请查看使用JVMTI创建调试和分析代理程序。
Java代码示例使用GarbageCollectorMXBean
,在接受的答案中进行了引用:
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);
}
}
看起来你可以使用MemoryPoolMXBean并将收集使用阈值设置为1。这样,每当gc运行且仍有至少一个字节的内存在使用时,您都会收到通知。
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/MemoryPoolMXBean.html
看起来这并不适用于所有垃圾收集器。
在收到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;
}
在Javalobby上有一篇有趣的文章,讨论了一种实现这个功能的方法。
对于您自己的程序来说,没有标准的方法可以从JVM获取垃圾回收的信息。任何此类API都是特定于供应商的。
为什么您发现的工具不足呢?
-Xloggc
: 从 jdk1.6 update 4 开始,您可以使用 -XX:+PrintGCDateStamps
让 Sun/Oracle JVM 打印出日期和时间。这使得日志更加有用,特别是如果您添加了一个可以通知您任何 GC 问题的日志扫描器/监视器。