Pregunta

¿Cómo se puede encontrar una pérdida de memoria en Java (usando, por ejemplo, JHat)?Intenté cargar el volcado del montón en JHat para echar un vistazo básico.Sin embargo, no entiendo cómo se supone que puedo encontrar la referencia raíz (árbitro) 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 así), pero los mapas se usan en todas partes...¿Hay alguna forma de buscar mapas grandes o quizás encontrar raíces generales de árboles de objetos grandes?

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 pagar por JProfiler).Además, JHat siempre está disponible ya que forma parte del JDK.A menos, por supuesto, que con JHat no haya otra manera que la fuerza bruta, pero no puedo creer que ese pueda ser el caso.

Además, no creo que pueda modificarlo (agregando el registro de todo tamaños de mapas) y ejecútelo durante el tiempo suficiente para que pueda notar la fuga.

¿Fue útil?

Solución

Utilizo el siguiente enfoque para encontrar pérdidas de memoria en Java.He usado 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 y la caché.
  3. Ejecute GC y tome una instantánea de la memoria.
  4. Ejecute la operación nuevamente.Dependiendo de la complejidad de la operación y del tamaño de los datos que se procesan, es posible que sea necesario ejecutar la operación varias o muchas veces.
  5. Ejecute GC y tome una instantánea de la memoria.
  6. Ejecute una diferencia para 2 instantáneas y analícela.

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

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

Hice varios proyectos específicamente destinados a reducir el uso de memoria de las aplicaciones y este enfoque general con algunos ajustes y trucos específicos de la aplicación siempre funcionó bien.

Otros consejos

Interlocutor aquí, debo decir que obtener una herramienta que no tarde 5 minutos en responder a cualquier clic hace que sea mucho más fácil encontrar posibles pérdidas de memoria.

Dado que la gente sugiere varias herramientas (solo probé Visual WM ya que lo obtuve en la versión de prueba de JDK y JProbe), pensé que debería sugerir una herramienta gratuita/de código abierto construida en la plataforma Eclipse, el analizador de memoria (a veces denominado módulo de memoria SAP). analizador) disponible en http://www.eclipse.org/mat/ .

Lo realmente interesante de esta herramienta es que indexó el volcado del montón cuando la abrí por primera vez, lo que le permitió mostrar datos como el montón retenido sin esperar 5 minutos para cada objeto (prácticamente todas las operaciones fueron muchísimo más rápidas que las otras herramientas que probé). .

Cuando abre el volcado, la primera pantalla le muestra un gráfico circular con los objetos más grandes (contando el montón retenido) y uno puede navegar rápidamente hasta los objetos que son demasiado grandes para su comodidad.También tiene una opción para buscar posibles sospechosos de fugas que creo que puede resultar útil, pero como la navegación fue suficiente para mí, no entré realmente en ello.

Una herramienta es de gran ayuda.

Sin embargo, hay ocasiones en las que no puedes utilizar una herramienta:el volcado del montón es tan grande que bloquea la herramienta, está intentando solucionar problemas de una máquina en algún entorno de producción al que solo tiene acceso de shell, etc.

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

Busque COMENZAR LOS SITIOS.Esto le muestra qué objetos están utilizando más memoria.Pero los objetos no se agrupan únicamente por tipo:cada entrada también incluye una identificación de "rastreo".Luego puede buscar ese "TRACE nnnn" para ver los primeros fotogramas de la pila donde se asignó el objeto.A menudo, una vez que veo dónde está asignado 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 -Xrunhprof.

Si revisa el sitio de asignación y no ve nada malo, debe comenzar a encadenar 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 un solo objeto raíz (es decir, un objeto que no está sujeto a recolección de basura).Los subprocesos, las clases y los marcos de pila actúan como objetos raíz y cualquier cosa a la que hagan referencia fuerte 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 apariciones de esa identificación para encontrar quién tiene una fuerte referencia al objeto.Siga cada uno de esos caminos hacia atrás a medida que se bifurcan hasta que descubra dónde está la fuga.¿Ves por qué una herramienta es tan útil?

Los miembros estáticos son reincidentes en cuanto a pérdidas de memoria.De hecho, incluso sin una herramienta, valdría la pena dedicar unos minutos a buscar en el código miembros estáticos del mapa.¿Puede un mapa crecer?¿Algo alguna vez 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 como máximo.Me ha resultado difícil hacer que el generador de perfiles de NetBeans funcione directamente en estas grandes aplicaciones de Java.

Pero normalmente esto no es necesario.Puede utilizar la utilidad jmap que viene con el jdk para realizar un volcado del montón "en vivo", es decir, jmap volcará el montón después de ejecutar GC.Realice alguna operación en la aplicación, espere hasta que se complete la operación y luego realice otro volcado del montón "en vivo".Utilice herramientas como Eclipse MAT para cargar los volcados de montón, ordenar el histograma, ver 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)

Sólo hay un problema con este enfoque;Los volcados de montón enormes, incluso con la opción en vivo, pueden ser demasiado grandes para transferirlos a la etapa de desarrollo y pueden necesitar una máquina con suficiente memoria/RAM para abrirse.

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

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

En lugar de realizar dos volcados de montón, tome dos histogramas de clase, como se describe anteriormente;Luego compare los histogramas de clases y vea las clases que están aumentando.Vea si puede relacionar las clases de Java con las clases de su aplicación.Esto le dará una buena pista.Aquí hay un script de Python que puede ayudarlo a comparar dos volcados de histogramas de 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 alto uso de memoria. Para esto, habilite el registro de GC; use un GC de compactación nuevo y más avanzado como G1GC;y puedes usar herramientas jdk como jstat para ver el comportamiento de GC en vivo

jstat -gccause pid <optional time interval>

Otras referencias a Google para -jhat, jmap, GC completo, asignación enorme, G1GC

Existen herramientas que deberían ayudarle a encontrar su fuga, como JProbe, YourKit, AD4J o JRockit Mission Control.Este último es el que personalmente conozco mejor.Cualquier buena herramienta debería permitirle profundizar hasta un nivel en el que pueda identificar fácilmente qué fugas y dónde están ubicados los objetos con fugas.

Usar HashTables, Hashmaps o similares es una de las pocas formas en que realmente puedes perder memoria en Java.Si tuviera que encontrar la fuga a mano, imprimiría periódicamente el tamaño de mis HashMaps y desde allí buscaría aquel en el que agrego elementos y me olvido de eliminarlos.

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

NetBeans tiene un generador de perfiles incorporado.

Realmente necesitas usar un generador de perfiles de memoria que rastree las asignaciones.Echa 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): rápidamente gastará $500 en tiempo luchando por encontrar una fuga con herramientas menos sofisticadas.

es posible que desees comprobarlo jconsola.También es parte del JDK y me ha resultado útil encontrar pérdidas de memoria/referencia junto con jhat.Échale también un vistazo a este Entrada de blog.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top