Cómo encontrar un escape de memoria de Java

¿Cómo se encuentra una pérdida de memoria en Java (utilizando, por ejemplo, JHat)? He intentado cargar el volcado del montón en JHat para darle un aspecto básico. Sin embargo, no entiendo cómo se supone que puedo encontrar la referencia de raíz ( ref ) o como se llame. Básicamente, puedo decir que hay varios cientos de megabytes de entradas de la tabla hash ([java.util.HashMap $ Entry o algo por el estilo), pero los mapas se usan por todos lados … ¿Hay alguna forma de buscar mapas grandes? , o tal vez encontrar raíces generales de grandes árboles objeto?

[Editar] Ok, he leído las respuestas hasta ahora, pero digamos que soy un bastardo barato (lo que significa que estoy más interesado en aprender a usar JHat que en pagar por JProfiler). Además, JHat siempre está disponible ya que es parte del JDK. A menos que, por supuesto, no haya forma con JHat sino con la fuerza bruta, pero no puedo creer que ese sea el caso.

Además, no creo que pueda modificar (agregar el registro de todos los tamaños de mapa) y ejecutarlo el tiempo suficiente para que note la fuga.

Utilizo el siguiente enfoque para encontrar memory leaks en Java. He utilizado jProfiler con gran éxito, pero creo que cualquier herramienta especializada con capacidades gráficas (las diferencias son más fáciles de analizar en forma gráfica) funcionará.

  1. Inicie la aplicación y espere hasta que llegue al estado “estable”, cuando se complete toda la inicialización y la aplicación esté inactiva.
  2. Ejecute la operación sospechosa de producir una pérdida de memoria varias veces para permitir que se realice cualquier inicialización relacionada con la base de datos de la memoria caché.
  3. Ejecute GC y tome una instantánea de memoria.
  4. Ejecute la operación nuevamente. Dependiendo de la complejidad de la operación y de los tamaños de datos procesados, es posible que la operación deba ejecutarse varias veces.
  5. Ejecute GC y tome una instantánea de memoria.
  6. Ejecuta un diff para 2 instantáneas y analízalo.

Básicamente, el análisis debe comenzar desde la máxima diferencia positiva por, digamos, tipos de objetos y encontrar qué causa que esos objetos adicionales se peguen en la memoria.

Para las aplicaciones web que procesan solicitudes en varios hilos, el análisis se vuelve más complicado, pero aún así se aplica el enfoque general.

Realicé bastantes proyectos específicamente dirigidos a reducir la huella de memoria de las aplicaciones y este enfoque general con algunos ajustes y trucos específicos de la aplicación siempre funcionó bien.

Interlocutor aquí, tengo que decir que obtener una herramienta que no tome 5 minutos para responder cualquier clic hace que sea mucho más fácil encontrar posibles memory leaks.

Como la gente sugiere varias herramientas (solo intenté wm visual desde que lo obtuve en la versión JDK y JProbe), creo que debería sugerir una herramienta gratuita / de código abierto creada en la plataforma Eclipse, el Memory Analyzer (a veces denominado SAP memory analizador) disponible en http://www.eclipse.org/mat/ .

Lo que es realmente genial de esta herramienta es que indizó el volcado de almacenamiento dynamic cuando lo abrí, lo que permitió mostrar datos como montón retenido sin esperar 5 minutos por cada objeto (casi todas las operaciones fueron mucho más rápidas que las otras herramientas que probé) .

Cuando abre el volcado, la primera pantalla muestra un gráfico circular con los objetos más grandes (contando el montón retenido) y uno puede navegar rápidamente hacia los objetos que son grandes para mayor comodidad. También tiene un hallazgo de posibles sospechosos de fugas, que reccon puede ser útil, pero como la navegación fue suficiente para mí, realmente no me metí en ello.

Una herramienta es de gran ayuda.

Sin embargo, hay momentos en los que no puede usar una herramienta: el volcado de almacenamiento dynamic es tan grande que bloquea la herramienta, está intentando solucionar un problema en una máquina en un entorno de producción al que solo tiene acceso de shell, etc.

En ese caso, es útil conocer el archivo de volcado de hprof.

Busque los SITIOS EMPIEZAN. Esto le muestra qué objetos están usando más memoria. Pero los objetos no se agrupan solo por tipo: cada entrada también incluye una ID de “rastreo”. A continuación, puede buscar ese “TRACE nnnn” para ver los primeros pocos cuadros de la stack donde se asignó el objeto. A menudo, una vez que veo dónde se asigna el objeto, encuentro un error y listo. Además, tenga en cuenta que puede controlar cuántos fotogtwigs se graban en la stack con las opciones para -Xrunhprof.

Si revisa el sitio de asignación y no ve nada incorrecto, debe iniciar el encadenamiento hacia atrás desde algunos de esos objetos activos hasta los objetos raíz, para encontrar la cadena de referencia inesperada. Aquí es donde una herramienta realmente ayuda, pero puedes hacer lo mismo a mano (bueno, con grep). No hay solo un objeto raíz (es decir, un objeto no sujeto a la recolección de basura). Los subprocesos, las clases y los marcos de stack actúan como objetos raíz, y todo lo que hacen referencia fuertemente no es coleccionable.

Para realizar el encadenamiento, busque en la sección HEAP DUMP las entradas con la id incorrecta de rastreo. Esto lo llevará a una entrada OBJ o ARR, que muestra un identificador de objeto único en hexadecimal. Busque todas las instancias de ese ID para encontrar quién tiene una referencia fuerte al objeto. Siga cada uno de esos caminos hacia atrás mientras se ramifican hasta que descubra dónde está la fuga. ¿Ves por qué una herramienta es tan útil?

Los miembros estáticos son reincidentes por memory leaks. De hecho, incluso sin una herramienta, valdría la pena pasar unos minutos mirando a través de su código los miembros estáticos de Map. ¿Puede un mapa crecer? ¿Alguna vez algo limpia sus entradas?

La mayoría de las veces, en las aplicaciones empresariales, el montón de Java proporcionado es mayor que el tamaño ideal de 12 a 16 GB máximo. Me ha resultado difícil hacer que el generador de perfiles de NetBeans funcione directamente en estas grandes aplicaciones de Java.

Pero generalmente esto no es necesario. Puede usar la utilidad jmap que viene con jdk para tomar un volcado dynamic “en vivo”, es decir, jmap va a volcar el montón después de ejecutar GC. Haga alguna operación en la aplicación, espere hasta que se complete la operación, luego tome otro volcado de stack “en vivo”. Use herramientas como Eclipse MAT para cargar los heapdumps, ordene el histogtwig, vea qué objetos han aumentado o cuáles son los más altos. Esto daría una pista.

 su proceeuser /bin/jmap -dump:live,format=b,file=/tmp/2930javaheap.hrpof 2930(pid of process) 

Solo hay un problema con este enfoque; Los volcados en gran cantidad, incluso con la opción en vivo, pueden ser demasiado grandes para transferirse a la vuelta de desarrollo, y pueden necesitar una máquina con suficiente memoria / RAM para abrir.

Ahí es donde el histogtwig de clase entra en la imagen. Puede volcar un histogtwig de clase en vivo con la herramienta jmap. Esto dará solo el histogtwig de clase de uso de memoria. Básicamente no tendrá la información para encadenar la referencia. Por ejemplo, puede poner una matriz de caracteres en la parte superior. Y la clase String en algún lugar debajo. Tienes que dibujar la conexión tú mismo.

 jdk/jdk1.6.0_38/bin/jmap -histo:live 60030 > /tmp/60030istolive1330.txt 

En lugar de tomar dos volcados del montón, tome dos histogtwigs de clase, como se describe arriba; Luego compare los histogtwigs de clase y vea las clases que están aumentando. Vea si puede relacionar las clases de Java con sus clases de aplicaciones. Esto dará una buena pista. Aquí hay un script de pitones que puede ayudarlo a comparar dos volcados de histogtwig jmap. histogramparser.py

Finalmente, herramientas como JConolse y VisualVm son esenciales para ver el crecimiento de la memoria a lo largo del tiempo y ver si hay una pérdida de memoria. Finalmente, a veces su problema puede no ser una pérdida de memoria, sino un uso de memoria alto. Para esto, habilite el registro de GC, use un GC de compactación más avanzado y nuevo como G1GC; y puedes usar herramientas jdk como jstat para ver el comportamiento del GC en vivo

 jstat -gccause pid  

Otras referencias a google para -jhat, jmap, GC completo, asignación de Humongous, G1GC

Hay herramientas que deberían ayudarlo a encontrar su fuga, como JProbe, YourKit, AD4J o JRockit Mission Control. El último es el que personalmente conozco mejor. Cualquier buena herramienta debería permitirle profundizar hasta un nivel donde pueda identificar fácilmente las fugas y dónde se asignan los objetos con fugas.

El uso de HashTables, Hashmaps o similar es una de las pocas formas en que se puede filtrar memoria en Java. Si tuviera que encontrar la fuga a mano imprimiría peridicamente el tamaño de mis HashMaps, y de allí encontraría el que agregue elementos y olvide eliminarlos.

Bueno, siempre existe la solución de baja tecnología de agregar el registro del tamaño de sus mapas cuando los modifica, y luego buscar en los registros cuyos mapas crecen más allá de un tamaño razonable.

Realmente necesita usar un perfilador de memoria que rastree las asignaciones. Eche un vistazo a JProfiler : su función “heap walker” es excelente y tienen integración con todos los principales IDE de Java. No es gratis, pero tampoco es tan caro ($ 499 por una sola licencia): gastará $ 500 en tiempo, luchando rápidamente para encontrar una fuga con herramientas menos sofisticadas.

NetBeans tiene un generador de perfiles integrado.

es posible que desee comprobar jconsole . También es parte del JDK y me ha resultado útil encontrar memory leaks / referencia junto con jhat. También eche un vistazo a esta entrada de blog.

    Intereting Posts