2010-01-13 2 views
32

Я хотел бы зарегистрировать обратный вызов с JVM, чтобы я знал, когда происходит сборка мусора. Есть какой-либо способ сделать это?Уведомление о сборе мусора?

EDIT: Я хочу сделать это, чтобы я мог выйти из системы, когда сбор мусора происходит в моем журнале приложений, поэтому я могу видеть, коррелирует ли это с проблемами, которые я вижу. Включение -Xloggc полезно, но немного сложнее интегрировать времена из журнала GC (которые используют секунды с момента запуска приложения) в мой основной журнал приложений.

EDIT Апрель 2012: Начиная с Java7u4, вы можете получать уведомления от GarbageCollectorMXBean (красивый example).

+0

В идеале, разработчик не должен заботиться, когда GC происходит. Вы можете сделать некоторые настройки в аргументах JVM. Просто из любопытства, что вы пытаетесь сделать? Возможно, есть лучшее событие для регистрации. – Jay

+13

@ Джей, мы не живем в идеальном мире. Если вы действительно заботитесь о том, чтобы ваша служба работала, вы, вероятно, захотите услышать, когда сбор мусора начинает происходить слишком часто, поскольку это может быть признаком проблем. – tster

+1

Хороший вопрос. Было бы неплохо, если бы уведомление GC могло быть интегрировано с балансировщиком нагрузки, чтобы избежать отправки трафика экземпляру во время GC. –

ответ

17

Начиная с Java7u4, вы можете получать уведомления от GarbageCollectorMXBean. См. http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html

+1

Стоит упомянуть, что часто быстрее наблюдать за MemoryPoolMXBean и видеть, когда падает память. – Xorlev

+0

Хотя MemoryPoolMXBean намного быстрее, вы просто не знаете, когда GC запускается.Если вам важно знать такие вещи, как использование максимальной памяти в вашем приложении, тогда вы захотите проверить использование памяти непосредственно до и после сбора мусора. Если вы опросите, вы не будете знать наверняка. Просто мои $ 0.02 :) – CodeBlind

1

Существует interesting article on Javalobby, где обсуждается один из способов этого.

+0

Интересный подход, но он вводит некоторые накладные расходы и уведомляет вас только о том, когда собирается определенный объект. Я хочу получить уведомление, когда происходит GC, с той же частотой, что и команда -Xloggc записывается в файл. –

3

Похоже, вы можете использовать MemoryPoolMXBean и установить порог использования коллекции на 1. Это должно дать вам уведомление в любое время, когда gc запускается, и по-прежнему имеется по крайней мере байт используемой памяти.

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

Похоже, что это не работает со всеми сборщиками мусора, хотя.

1

Нет стандартного способа для вашей собственной программы получать информацию от JVM о сборе мусора. Любой такой API специфичен для конкретного поставщика.

Почему объект оказался недостаточно?

+0

JVM TI является стандартным для Java 5+, если я правильно помню. Вы хотите, чтобы программа слушала * себя с этим? –

+0

Я не могу найти конечную ссылку, но JVM TI, похоже, реализуется большинством JVM: Sun, IBM (http://publib.boulder.ibm.com/infocenter/javasdk/v6r0/index.jsp?topic=/ com.ibm.java.doc.diagnostics.60/diag/tools/jvmti.html), Oracle/BEA, Harmony (и, возможно, больше). –

+0

О, я понимаю, что вы имели в виду. Но агент может быть вашей собственной программой :) –

0

Если вы рассматриваете это как инструмент диагностики, я рекомендую перенаправить ваш журнал приложений в StdOut, а затем перенаправить StdOut и StdErr в файл. Это даст вам подробную информацию о регистрации JVM, не заставляя вас изменять код приложения.

+0

Как я написал это, я начал задаваться вопросом, возможно ли, что JVM-журнал может быть перенаправлен в вашу инфраструктуру регистрации приложений. Что вы используете для регистратора приложений? – kdgregory

+0

Мы используем log4j –

10

Я думаю, что стандартный способ - использовать JVM Tool Interface (JVM TI), чтобы написать агент с обратным вызовом GC start и зарегистрировать время от него (см. GetTime). Обратите внимание, что Garbage Collection Start event отправляется только для полных GC.

Примеры агентов 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.

+0

Хорошее предложение; Я не слышал о JVM TI. Однако наше приложение почти никогда не делает полного gc, поэтому я хочу знать о коллекциях gen 1. Что вы думаете о идее MemoryPoolMXBean? –

2

При получении события JVMTI для коллекции мусора 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; 
} 
1

Что касается -Xloggc: На обновление jdk1.6 4 вы можете получить Sun/Oracle JVM, чтобы распечатать дату и время, используя -XX:+PrintGCDateStamps. Это значительно облегчает использование журналов , особенно если вы добавляете сканер/монитор журнала, который может уведомить вас о любых проблемах с GC.

2

Другой случай использования для получения надвигающиеся уведомления GC: если ваше приложение для балансировки нагрузки, то вы можете уведомить балансировки нагрузки, чтобы взять узел из бассейна, когда GC собирается начать с того, что он не получает запросы, которые должны были бы ждать полного GC, с которым можно было бы справиться.

Это не помогает запросам в полете, которые прибыли непосредственно перед тем, как GC заработал, но, по крайней мере, в моем случае, большинство запросов являются субсекундами, а основные GC - 5-10 секунд, каждые несколько минут. Мы можем настроить коэффициенты NewGen и т. Д., Но общий момент по-прежнему применим: основной GC может быть намного длиннее обычного времени ответа, поэтому вы можете заранее остановить узел, запускающий основной GC от получения запросов.

Когда GC завершен, поток в JVM может отправить уведомление балансировщику нагрузки, чтобы сообщить ему об этом в бизнесе, или LB может полагаться на свой обычный keepalive.

2

Я знаю, что это очень поздно, но я надеюсь, что это когда-нибудь может кому-то помочь.

Эти события можно получить, используя библиотеку, которую я разрабатываю под названием gcRadar. Он предоставляет информацию о том, когда именно объект был собран мусором.

Любые предложения по улучшению в библиотеке приветствуются.

4

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); 
    } 
} 

site that has detailed sample code that uses the GarbageCollectorMXBean.

Смежные вопросы