2008-09-02 21 views
120

¿Cómo se puede encontrar 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 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?Cómo encontrar un error de memoria de Java

[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 realmente (agregando el registro de todos los tamaños de mapa) y ejecutarlo el tiempo suficiente para que note la fuga.

+0

Oracle tiene una página relevante aquí: https: //docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html#BABIIIAC – Laurel

+0

Este es otro "voto" para JProfiler. Funciona bastante bien para el análisis de montón, tiene una interfaz de usuario decente y funciona bastante bien. Como dice McKenzieG1, $ 500 es más barato que la cantidad de tiempo que de lo contrario podrías quemar buscando la fuente de estas fugas. En cuanto al precio de las herramientas, no está mal. – joev

Respuesta

115

Utilizo el siguiente enfoque para encontrar fugas de memoria 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 haya completado 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 cualquier caché, inicialización relacionada con DB, tenga lugar.
  3. Ejecute GC y tome la instantánea de memoria.
  4. Ejecute la operación de nuevo. 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 la instantánea de memoria.
  6. Ejecuta un diff para 2 instantáneas y analízalo.

Básicamente, el análisis debe comenzar desde la diferencia positiva más grande por, digamos, tipos de objetos y encontrar qué hace 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 destinados 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.

+4

¿Podría decirme cómo ejecutar el GC manualmente? –

+7

La mayoría (si no todos) de los perfiles de Java le ofrecen una opción para invocar GC con un clic de un botón. O puede llamar a System.gc() desde el lugar apropiado en su código. –

+3

Incluso si llamamos al sistema.gc() JVM puede elegir descuidar la llamada. AFAIK esto es específico de JVM. +1 a la respuesta. –

4

Bueno, no es siempre la solución de baja tecnología de añadir el registro del tamaño de los mapas cuando se modifica, a continuación, buscar en los registros para los que los mapas están creciendo más allá de un tamaño razonable.

0

que realmente necesita usar un perfilador de memoria que registra las asignaciones. Eche un vistazo a JProfiler - su característica "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.

5

hay herramientas que le ayudarán 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 tablas hash, HashMaps o similar, es una de las pocas formas que puede presentar fugas acually de memoria en Java en absoluto. 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.

0

NetBeans tiene un generador de perfiles incorporado.

12

Una herramienta es de gran ayuda.

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

En ese caso, es útil saber cómo funciona todo el archivo de volcado hprof.

Busque los SITIOS COMIENZAN. 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 pila 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 fotogramas se graban en la pila con las opciones para -Xrunhprof.

Si se echa un vistazo al sitio de la asignación, y no se ve nada mal, usted tiene que comenzar encadenamiento hacia atrás de algunos de esos objetos vivos a objetos raíz, para encontrar la cadena de referencia inesperado. 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 pila 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 identificación de seguimiento incorrecta. 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 infractores reincidentes por fugas de memoria. 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?

+0

"el volcado del heap es tan grande que bloquea la herramienta" -la última vez que lo comprobé, 'jhat' y' MAT' aparentemente intentan cargar todo el volcado del montón en la memoria, y típicamente se cuelga con un 'OutOfMemoryError' en volcados grandes (es decir, , de las aplicaciones que más necesitaban el análisis de heap!). El NetBeans Profiler parece usar un algoritmo diferente para indexar referencias que pueden obtener _slow_ en volcados grandes pero al menos no consume memoria ilimitada en la herramienta y falla. –

-1

es posible que desee comprobar jconsole. También es parte del JDK y me ha resultado útil encontrar fugas de memoria/referencia junto con jhat. También echa un vistazo a la entrada del blog this.

46

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 pérdidas de memoria.

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

Lo que es realmente genial de esta herramienta es que indexó el volcado del montón cuando lo abrí por primera vez, lo que permitió mostrar datos como montón retenido sin esperar 5 minutos para cada objeto (casi todas las operaciones fueron toneladas más rápidas que el otro 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.

+0

Digno de mención: aparentemente en Java 5 y superior, el parámetro 'HeapDumpOnCtrlBreak' VM * no está disponible *. La solución que he encontrado (hasta el momento, sigue buscando) es usar JMap para volcar el archivo '.hprof', que luego pongo en Eclipse y uso MAT para examinar. – Ben

+1

En cuanto a la obtención del volcado de heap, la mayoría de los perfiladores (incluido JVisualVM) incluyen la opción de volcar tanto el montón como los subprocesos en un archivo. – bbaja42

3

La mayoría de las veces, en aplicaciones empresariales, el almacenamiento dinámico de Java 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 dinámico "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 pila "en vivo". Use herramientas como Eclipse MAT para cargar los heapdumps, ordene el histograma, 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.

Aquí es donde entra en juego el histograma de clase. Puede volcar un histograma de clase en vivo con la herramienta jmap. Esto dará solo el histograma 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 vuelcos del montón, tome dos histogramas de clase, como se describe anteriormente; Luego compare los histogramas 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 histograma jmap. histogramparser.py

Finalmente, herramientas como JConolse y VisualVm son esenciales para ver el crecimiento de la memoria con el 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 se puede utilizar como herramientas de JDK jstat para ver el comportamiento GC vivo

jstat -gccause pid <optional time interval> 

Otros referecences a Google para -jhat, jmap, GC completo, la asignación de Humongous, G1GC

+0

agregó una publicación de blog con más detalles aquí - http://alexpunnen.blogspot.in/2015/06/long-running-java-process-resource.html –

Cuestiones relacionadas