Ответ 1
С Java7u4 вы можете получать уведомления из GarbageCollectorMXBean. См. http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html
Я хотел бы зарегистрировать обратный вызов с JVM, чтобы я знал, когда происходит сбор мусора. Есть ли способ сделать это?
EDIT: Я хочу сделать это, чтобы я мог выйти из системы, когда сбор мусора происходит в моем журнале приложений, поэтому я могу видеть, коррелирует ли это с проблемами, которые я вижу. Включение -Xloggc полезно, но немного сложнее интегрировать времена из журнала GC (которые используют секунды с момента запуска приложения) в мой основной журнал приложений.
EDIT Апрель 2012: Начиная с Java7u4, вы можете получать уведомления от GarbageCollectorMXBean (хороший пример).
С Java7u4 вы можете получать уведомления из GarbageCollectorMXBean. См. http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html
Я думаю, что стандартным способом является использование JVM Tool Interface (JVM TI) для записи агента с обратным вызовом GC start и запишите время из него (см. GetTime). Обратите внимание, что Событие запуска коллекции мусора отправляется только для полных GC.
Примеры агентов JVM TI доступны в демо-каталоге JDK 5.0 или загрузки JDK 6. Техническая статья Интерфейс JVM Tool (JVM TI): как работают агенты VM - еще один очень ресурс. Также посмотрите Создание агента отладки и профилирования с помощью JVMTI.
Похоже, вы можете использовать MemoryPoolMXBean и установить порог использования коллекции на 1. Это должно дать вам уведомление в любое время, когда gc запускается, и по-прежнему имеется по крайней мере байт используемой памяти.
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/MemoryPoolMXBean.html
Похоже, что это не работает со всеми сборщиками мусора.
Пример кода 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);
}
}
сайт с подробным примером кода, который использует GarbageCollectorMXBean
.
При получении события JVMTI для Garbage Collection JVM технически прекращается, поэтому он не может перезвонить слушателю Java через JNI.... этот агент печатает время при запуске и завершении 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;
}
Еще один пример использования для получения ожидающих уведомлений GC: если ваше приложение сбалансировано по нагрузке, тогда вы можете затем уведомить балансировщик нагрузки о том, чтобы вывести ваш node из пула, когда GC собирается начать с того, что он не получает запросы что придется ждать полного GC, с которым можно будет справиться.
Это не помогает запросам в полете, которые прибыли непосредственно перед тем, как GC взорвался, но, по крайней мере, в моем случае, большинство запросов являются субсекундами, а основные GC - 5-10 секунд, каждые несколько минут. Мы можем настроить коэффициенты NewGen и т.д., Но общий момент по-прежнему применяется: основной GC может быть намного длиннее обычного времени отклика, поэтому вы можете предварительно остановить node, начиная основной GC от получения запросов.
Когда GC завершен, поток в JVM может отправить уведомление балансировщику нагрузки, чтобы он знал его обратно в бизнесе, или LB может полагаться на свой обычный keepalive.
Я знаю, что это очень поздно, но я надеюсь, что это когда-нибудь поможет кому-нибудь.
Вы можете получить такие события с помощью использования библиотеки, которую я разрабатываю, называемой gcRadar. Он предоставляет информацию о том, когда именно объект был собран мусором.
Любые предложения по улучшению в библиотеке приветствуются.
Там интересная статья о Джавальбае, обсуждая один из способов этого.
Нет стандартного способа для вашей собственной программы получать информацию от JVM о сборе мусора. Любой такой API специфичен для поставщика.
Почему средство, которое вы обнаружили недостаточно?
Относительно -Xloggc
: Начиная с версии 4 jdk1.6 вы можете заставить Sun/Oracle JVM распечатать дату и время с помощью -XX:+PrintGCDateStamps
. Это делает журналы намного более полезными, особенно если вы добавляете сканер/монитор журнала, который может уведомить вас о любых проблемах с GC.
Если вы рассматриваете это как инструмент диагностики, я рекомендую перенаправить ваш журнал приложений в StdOut, а затем перенаправить StdOut и StdErr в файл. Это даст вам подробную информацию о регистрации JVM, не заставляя вас менять код приложения.