2010-01-13 16 views
32

Me gustaría registrar una devolución de llamada con la JVM, así sé cuándo se producirá la recolección de basura. ¿Hay alguna manera de hacer esto?¿Notificación de recolección de basura?

EDITAR: Quiero hacer esto para poder desconectarme cuando ocurra la recolección de basura en el registro de mi aplicación, para ver si se relaciona con los problemas que estoy viendo. Activar -Xloggc es útil, pero es un poco complicado integrar los tiempos del registro de GC (que usan segundos desde el inicio de la aplicación) en el registro de mi aplicación principal.

EDITAR Abril de 2012: a partir de Java7u4, puede obtener notificaciones de GarbageCollectorMXBean (un buen example).

+0

ideal es que un desarrollador no debería tener que preocuparse cuando sucede GC. Puede hacer algunos ajustes en los argumentos de JVM. Solo por curiosidad, ¿qué estás tratando de hacer? Tal vez haya un evento mejor para registrarse. – Jay

+13

@Jay, no vivimos en un mundo ideal. Si realmente le preocupa que su servicio funcione, es probable que desee alarmar cuando la recolección de basura comienza a suceder con demasiada frecuencia, ya que puede ser un signo de problemas. – tster

+1

Buena pregunta. Sería bueno si la notificación de GC se pudiera integrar con un equilibrador de carga para evitar enviar tráfico a la instancia durante GC. –

Respuesta

17

A partir de Java7u4, puede obtener notificaciones de GarbageCollectorMXBean. Ver http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/GarbageCollectionNotificationInfo.html

+1

Vale la pena mencionar que a menudo es más rápido ver MemoryPoolMXBean y ver cuándo se reduce el uso de memoria. – Xorlev

+0

Aunque MemoryPoolMXBean es mucho más rápido, simplemente no sabe cuándo se ejecuta el GC.Si es importante que conozca cosas como el uso máximo de memoria de su aplicación, querrá verificar el uso de la memoria inmediatamente antes y después de la recolección de basura. Si sondeas, no lo sabrás con certeza. Solo mi $ 0.02 :) – CodeBlind

1

Hay un interesting article on Javalobby que discute un método para hacerlo.

+0

Enfoque interesante, pero introduce cierta sobrecarga y solo le avisa cuando se recopila un objeto específico. Quiero una notificación cuando se produce GC, con la misma frecuencia que el comando -Xloggc se registra en un archivo. –

1

No hay una forma estándar para que su propio programa obtenga información de la JVM sobre la recolección de basura. Cualquier API de este tipo es específica del proveedor.

¿Por qué las instalaciones son insuficientes?

+0

El JVM TI es estándar para Java 5+ si recuerdo correctamente. ¿Le gustaría un programa para escuchar * * * con eso? –

+0

No encuentro el enlace definitivo, pero la JVM TI parece ser implementada por la mayoría de las 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 (y tal vez más). –

+0

Oh, ya veo lo que querías decir. Pero un agente puede ser su propio programa :) –

0

Si está viendo esto como una herramienta de diagnóstico, recomiendo redireccionar el registro de la aplicación a StdOut y luego redirigir StdOut y StdErr a un archivo. Esto le dará los detalles del registro de JVM, sin forzarle a cambiar el código de su aplicación.

+0

Cuando escribí esto, comencé a preguntarme si el registro de JVM podría redirigirse al marco de registro de aplicaciones. ¿Qué estás usando para un registrador de aplicaciones? – kdgregory

+0

Estamos utilizando log4j –

10

Creo que la forma estándar es usar el JVM Tool Interface (JVM TI) para escribir un agente con una devolución de llamada de inicio de GC y registrar la hora desde allí (consulte GetTime). Tenga en cuenta que se envía un Garbage Collection Start event solo para GC completos.

Los ejemplos de agentes JVM TI están disponibles en el directorio de demostración de la descarga de JDK 5.0 o JDK 6. El artículo técnico The JVM Tool Interface (JVM TI): How VM Agents Work es otro recurso muy. También eche un vistazo al Creating a Debugging and Profiling Agent with JVMTI.

+0

Buena sugerencia; No había oído hablar de JVM TI. Sin embargo, nuestra aplicación casi nunca hace un gc completo, por lo que quiero saber acerca de las colecciones gen 1. ¿Qué piensas de la idea de MemoryPoolMXBean? –

2

Al recibir el evento JVMTI para Garbage Collection, la JVM se detiene técnicamente por lo que no puede devolver la llamada a un oyente Java a través de JNI .... este agente imprime la hora en el inicio de GC y termina con una resolución mayor que la detallada GC en el 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; 
} 
2

Otro caso de uso para conseguir inminentes notificaciones GC: si su aplicación se encuentra balanceado entonces usted puede entonces notificar al equilibrador de carga para llevar a su nodo fuera de la piscina cuando una GC está a punto de comenzar a que no reciba solicitudes que tendrían que esperar a que se solucione un GC completo.

Eso no ayuda a las solicitudes de vuelo que llegaron justo antes de que el GC iniciara, pero, en mi caso al menos, la mayoría de las solicitudes son subsegundas y los GC principales son 5-10s, cada pocos minutos. Podemos modificar las relaciones NewGen, etc., pero el punto general sigue siendo válido: el GC principal puede ser mucho más largo que los tiempos de respuesta típicos, por lo que es recomendable que premptively detenga un nodo que comience a recibir un GC principal.

Cuando termina el GC, un hilo en la JVM puede enviar una notificación al equilibrador de carga para informarle que está de vuelta en el negocio o el LB puede confiar en su estado de alerta habitual.

2

Sé que esto es muy tarde, pero espero que pueda ayudar a alguien algún día.

Puede recibir estos eventos utilizando la biblioteca que estoy desarrollando llamada gcRadar. Proporciona información sobre cuándo se recolectó un objeto exactamente.

Cualquier sugerencia para mejoras en la biblioteca es bienvenida.

4

ejemplo de código Java utilizando laGarbageCollectorMXBean se hace referencia en la respuesta aceptada:

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.