español - garbage collector java example
¿Notificación de recolección de basura? (11)
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
Me gustaría registrar una devolución de llamada con JVM para saber cuándo se está realizando la recolección de basura. ¿Hay alguna manera de hacer esto?
EDITAR: Quiero hacer esto para poder desconectarme cuando ocurra la recolección de basura en el registro de mi aplicación, para poder 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 ).
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 del GC y finaliza con una resolución mayor que el detallado 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;
}
Con respecto a -Xloggc
: a partir de la actualización 4 de jdk1.6, puede obtener la JVM de Sun / Oracle para imprimir la fecha y la hora con -XX:+PrintGCDateStamps
. Esto hace que los registros sean mucho más útiles, especialmente si agrega un escáner / monitor de registro que puede notificarle sobre cualquier problema de GC.
Creo que la forma estándar es utilizar la interfaz de la herramienta JVM (JVM TI) para escribir un agente con una devolución de llamada de inicio de GC y registrar la hora a partir de él (ver GetTime ). Tenga en cuenta que un evento de inicio de recolección de basura se envía solo para GC completos.
Los agentes JVM TI de muestra están disponibles en el directorio de demostración de la descarga de JDK 5.0 o JDK 6. El artículo técnico La JVM Tool Interface (JVM TI): Cómo funcionan los agentes de VM es otro muy buen recurso. También eche un vistazo a Crear un agente de depuración y creación de perfiles con JVMTI .
Hay un interesante artículo sobre Javalobby que analiza un método para hacer esto.
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?
Otro caso de uso para recibir notificaciones GC inminentes: si su aplicación está balanceada de carga, entonces puede notificar al equilibrador de carga que retire su nodo del conjunto cuando un GC está a punto de comenzar y no recibe solicitudes que tendrían que esperar para un GC completo para ser tratado.
Eso no ayuda a las solicitudes en vuelo que llegaron justo antes de que el GC entrara en marcha, pero, en mi caso al menos, la mayoría de las solicitudes son subsegundas y los GC principales son de 5 a 10 segundos, cada pocos minutos. Podemos ajustar las relaciones de NewGen, etc., pero el punto general aún se aplica: el GC principal puede ser mucho más largo que los tiempos de respuesta típicos, por lo que es recomendable suspender de manera preventiva un nodo que inicia un GC principal para recibir solicitudes.
Cuando el GC termina, 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.
Parece que podría usar MemoryPoolMXBean y establecer el umbral de uso de la colección en 1. Esto debería darle una notificación cada vez que se ejecuta el gc y todavía hay al menos un byte de memoria en uso.
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/management/MemoryPoolMXBean.html
Sin embargo, parece que esto no funciona con todos los recolectores de basura.
Sé que es muy tarde, pero espero que pueda ayudar a alguien algún día.
Puede recibir estos eventos utilizando el uso de 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.
Si está viendo esto como una herramienta de diagnóstico, le recomiendo redirigir el registro de su aplicación a StdOut y luego redireccionar 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.
GarbageCollectorMXBean
código Java usando GarbageCollectorMXBean
al que se GarbageCollectorMXBean
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);
}
}