我想在JVM上注册一个回调,所以我知道垃圾收集何时发生。有没有办法做到这一点?
编辑:我想这样做,所以当我的应用程序日志中发生垃圾收集时我可以注销,所以我可以看到它是否与我看到的问题有关。打开-Xloggc很有帮助,但是将GC日志(自应用程序启动后使用秒数)的时间集成到我的主应用程序日志中有点棘手。编辑2012年4月:从Java7u4开始,您可以从GarbageCollectorMXBean(一个不错的example)获取通知。
答案 0 :(得分:18)
从Java7u4开始,您可以从GarbageCollectorMXBean获取通知。见http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html
答案 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)
答案 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日志记录的详细信息,而无需强制您更改应用程序代码。