2010-03-24 16 views
27

Tengo que encontrar una pérdida de memoria en una aplicación Java. Tengo algo de experiencia con esto, pero me gustaría recibir consejos sobre una metodología/estrategia para esto. Cualquier referencia y consejo es bienvenido.Método para encontrar la pérdida de memoria en grandes volcados de almacenamiento dinámico de Java

de nuestra situación:

  1. vuelcos de almacenamiento dinámico de más de 1 GB
  2. Tenemos montón vertederos de 5 ocasiones.
  3. No tenemos ningún caso de prueba para provocar esto. Solo ocurre en el entorno de prueba del sistema (masivo) después de al menos un uso de semanas.
  4. El sistema está construido sobre un marco heredado desarrollado internamente con tantos defectos de diseño que es imposible contarlos todos.
  5. Nadie entiende el marco en profundidad. Ha sido transferido a un tipo en India que apenas se mantiene al día respondiendo correos electrónicos.
  6. Hemos realizado volcados de almacenamiento dinámico de instantáneas con el tiempo y hemos llegado a la conclusión de que no hay un solo componente que aumente con el tiempo. Es todo lo que crece lentamente.
  7. Lo anterior nos señala en la dirección en que es el sistema ORM de framework propio que aumenta su uso sin límites. (Este sistema asigna los objetos a los archivos ?! Así que no es realmente un ORM)

Pregunta:¿Cuál es la metodología que ayudó a tener éxito con la caza de fugas en una aplicación de escala de la empresa?

+0

Compré más memoria – LB40

+0

@LB Tenemos 64 GB, pero el presupuesto para esta aplicación es de solo 2 GB, y no podemos aumentar razonablemente más de un par de GB sin comenzar a canibalizar en otros subsistemas. –

+0

Los volcados de montón son específicos de JVM, por lo tanto, debe utilizar la herramienta adecuada para la JVM en cuestión. ¿Es? –

Respuesta

47

Es casi imposible sin alguna comprensión del código subyacente. Si comprende el código subyacente, entonces puede ordenar mejor el trigo de la chatarra de los tropecientos trillizos de información que está obteniendo en sus vertederos.

Además, no se puede saber si algo es una fuga o no, sin saber por qué la clase está allí en primer lugar.

Acabo de pasar las últimas dos semanas haciendo exactamente esto, y utilicé un proceso iterativo.

Primero, los perfiladores de montón me parecieron básicamente inútiles. No pueden analizar los montones enormes de manera eficiente.

Más bien, me basé casi exclusivamente en jmap histogramas.

me imagine que está familiarizado con estos, pero para los que no:

jmap -histo:live <pid> > dump.out 

crea un histograma de la pila en vivo. En pocas palabras, te dice los nombres de las clases, y cuántas instancias de cada clase están en el montón.

Estaba abandonando montón regularmente, cada 5 minutos, 24 horas al día. Eso puede ser demasiado granular para ti, pero la esencia es la misma.

Realicé varios análisis diferentes de estos datos.

Escribí un script para tomar dos histogramas y eliminar la diferencia entre ellos. Entonces, si java.lang.String fuera 10 en el primer volcado y 15 en el segundo, mi secuencia de comandos escupiría "5 java.lang.String", y me dijo que subió 5 veces. Si se hubiera reducido, el el número sería negativo.

Entonces tomaría varias de estas diferencias, eliminaría todas las clases que pasaron de ejecución a ejecución y tomaré una unión del resultado. Al final, tendría una lista de clases que crecía continuamente en un lapso de tiempo específico. Obviamente, estos son los principales candidatos para las clases con pérdidas.

Sin embargo, algunas clases tienen algunas conservadas mientras que otras son GC'd. Estas clases podrían subir y bajar fácilmente en general, y aun así tener fugas. Por lo tanto, podrían caerse de la categoría de clases "siempre ascendente".

Para encontrarlos, convertí los datos en una serie temporal y la cargué en una base de datos, específicamente Postgres. Postgres es útil porque ofrece statistical aggregate functions, por lo que puede hacer simple linear regression analysis en los datos, y encontrar clases que tienden hacia arriba, incluso si no siempre están en la parte superior de los gráficos. Usé la función regr_slope, buscando clases con una pendiente positiva.

Encontré este proceso muy exitoso y realmente eficiente. Los archivos de histogramas no son increíblemente grandes y fue fácil descargarlos de los hosts. No eran demasiado caros para funcionar en el sistema de producción (forzaron un GC grande y pueden bloquear la VM por un tiempo). Estaba ejecutando esto en un sistema con un montón Java 2G.

Ahora, todo lo que se puede hacer es identificar las clases potencialmente con fugas.

Aquí es donde se entiende cómo se usan las clases, y si deberían o no deberían ser suyas.

Por ejemplo, es posible que tenga muchas clases de Map.Entry o alguna otra clase de sistema.

A menos que simplemente esté almacenando en caché String, el hecho es que estas clases de sistema, aunque quizás los "delincuentes", no son el "problema". Si está almacenando en caché alguna clase de aplicación, ESA clase es un mejor indicador de dónde reside su problema. Si no almacena en caché com.app.yourbean, entonces no tendrá asociado Map.Entry.

Una vez que tenga algunas clases, puede comenzar a rastrear el código base buscando instancias y referencias. Como tiene su propia capa de ORM (para bien o para mal), al menos puede mirar fácilmente el código fuente. Si ORM está almacenando en caché cosas, es probable que almacene en caché las clases de ORM que envuelven sus clases de aplicación.

Finalmente, otra cosa que puede hacer, una vez que conozca las clases, puede iniciar una instancia local del servidor, con un montón mucho más pequeño y un conjunto de datos más pequeño, y utilizando uno de los perfiles contra eso.

En este caso, puede realizar una prueba unitaria que afecte solo a 1 (o a un número pequeño) de las cosas que cree que pueden estar filtrando. Por ejemplo, puede iniciar el servidor, ejecutar un histograma, realizar una sola acción y ejecutar el histograma de nuevo. Tu clase de fuga debería haber aumentado en 1 (o lo que sea que sea tu unidad de trabajo).

Un generador de perfiles puede ayudarlo a rastrear a los propietarios de esa clase "ahora filtrada".

Pero, al final, vas a tener que entender algo de tu código base para comprender mejor qué es una fuga y qué no, y por qué existe un objeto en el montón, y mucho menos por qué puede estar siendo retenido como una fuga en tu montón.

13

Eche un vistazo a Eclipse Memory Analyzer. Es una gran herramienta (y autónoma, no requiere Eclipse sí mismo instalado) que 1) puede abrir montones muy grandes muy rápido y 2) tiene algunas herramientas de detección automática bastante buenas. El último no es perfecto, pero EMA ofrece muchas maneras realmente agradables de navegar y consultar los objetos en el vertedero para encontrar posibles fugas.

Lo he usado en el pasado para ayudar a buscar fugas sospechosas.

+0

Lo usé para analizar con éxito un depósito de ~ 180 megas ayer, funciona como un amuleto. – Esko

+0

Eclipse MAT es increíble, especialmente su Detector de fuga de memoria. –

+1

Sí, esto es lo que estamos usando principalmente. Funciona muy bien con al menos 1,5 GB de volcados de almacenamiento dinámico en Linux de 64 bits (por supuesto, Win 32 bit falla rápidamente). El único inconveniente es que no recibí mucha ayuda útil del análisis automatizado. –

1

Si está sucediendo después de una semana de uso, y su aplicación es tan bizantina como usted describe, ¿quizás es mejor reiniciarla cada semana?

Sé que no está solucionando el problema, pero puede ser una solución eficaz en el tiempo. ¿Hay ventanas de tiempo cuando puede tener interrupciones? ¿Se puede equilibrar la carga y fallar en una instancia mientras se mantiene el segundo? Tal vez pueda desencadenar un reinicio cuando el consumo de memoria supera un límite determinado (tal vez la supervisión a través de JMX o similar).

+0

¡La solución de Windows! (Nuestro departamento de TI usa esto para nuestros servidores de Windows) No ejecutamos el sistema nosotros mismos, sino que se vende a compañías que no pueden aceptar reinicios (programados o no programados). El simple signo de inestabilidad causaría amenazas de multas. –

+0

No me gusta, pero es pragmático en algunos escenarios. Sin embargo, observo su punto acerca de venderle a las compañías. –

+0

Acepto que puede ser una solución (temporal) en algunos casos. –

3

¿Se puede acelerar el tiempo? es decir, ¿puede escribir un cliente de pruebas ficticias que lo obligue a hacer llamadas o solicitudes durante una semana, etc. en unos minutos u horas? Estos son tus mejores amigos y si no tienes uno, escribe uno.

Utilizamos Netbeans hace un tiempo para analizar los volcados de almacenamiento dinámico. Puede ser un poco lento pero fue efectivo. Eclipse simplemente se colgó y las herramientas de 32 bits de Windows también.

Si tiene acceso a un sistema de 64 bits o un sistema Linux con 3 GB o más, le resultará más fácil analizar los volcados de almacenamiento dinámico.

¿Tiene acceso para cambiar los registros y los informes de incidentes?Los equipos de gestión de incidentes y gestión de incidentes normalmente contarán con empresas de gran escala, lo que puede resultar útil para rastrear los problemas que comenzaron a ocurrir.

¿Cuándo comenzó a salir mal? Habla con la gente e intenta obtener algo de historia. Puede hacer que alguien diga: "Sí, fue después de que arreglaron XYZ en el parche 6.43 que sucedieron cosas raras".

+0

Pensamos que debería ser una buena idea, pero en nuestro caso es inviable en el hoyo. Solo podemos ejecutar algunos casos de prueba con más frecuencia. La prueba del sistema solo se ejecuta cada 6 meses más o menos y la última vez que decidieron hacerlo más intenso. Después de esto, encontramos el problema. Intentamos degradar el marco y la aplicación a una versión que pasó la prueba antes. Las tres pruebas fallaron, esto nos dice que la falla es éter en otro componente del sistema o que ha estado en la nuestra durante mucho tiempo. Otro componente es poco probable. –

2

He tenido éxito con IBM Heap Analyzer. Ofrece varias vistas del montón, incluida la mayor caída en el tamaño del objeto, los objetos que se producen con mayor frecuencia y los objetos ordenados por tamaño.

0

He utilizado jhat, esto es un poco duro, pero depende del tipo de marco que tengas.

+0

No logramos cargar un volcado de gran tamaño en jhat y parece ser un problema común. Además, como lo recuerdo, cuando lo usé hace dos años, era un poco lento en conjuntos de datos más grandes. –

+0

como otra anécdota También he tenido problemas de carga con jhat y largeish (1G) montones –

+0

¿qué tipo de problema? problema de espacio de montón en el jvm corriendo jhat? – LB40

5

Esta respuesta se expande sobre @ Will-Hartung's. Solicité el mismo proceso para diagnosticar una de mis pérdidas de memoria y pensé que compartir los detalles ahorraría tiempo a otras personas.

La idea es tener tiempo de 'trama' postgres vs.uso de la memoria de cada clase, dibujar una línea que resume el crecimiento e identificar los objetos que están creciendo más rápido:

^
    | 
s | Legend: 
i | * - data point 
z | -- - trend 
e | 
( | 
b |     * 
y |      -- 
t |     -- 
e |    * -- * 
s |   -- 
) |  *--  * 
    |  -- * 
    | -- * 
    ---------------------------------------> 
         time 

convertir sus vuelcos de almacenamiento dinámico (necesitará múltiple) en un formato que es conveniente para el consumo de postgres desde el formato de volcado del montón:

num  #instances   #bytes class name 
---------------------------------------------- 
    1:  4632416  392305928 [C 
    2:  6509258  208296256 java.util.HashMap$Node 
    3:  4615599  110774376 java.lang.String 
    5:   16856  68812488 [B 
    6:  278914  67329632 [Ljava.util.HashMap$Node; 
    7:  1297968  62302464 
... 

a un archivo CSV con la fecha y hora de cada volcado de pila:

2016.09.20 17:33:40,[C,4632416,392305928 
2016.09.20 17:33:40,java.util.HashMap$Node,6509258,208296256 
2016.09.20 17:33:40,java.lang.String,4615599,110774376 
2016.09.20 17:33:40,[B,16856,68812488 
... 

el uso de este script:

# Example invocation: convert.heap.hist.to.csv.pl -f heap.2016.09.20.17.33.40.txt -dt "2016.09.20 17:33:40" >> heap.csv 

my $file; 
my $dt; 
GetOptions (
    "f=s" => \$file, 
    "dt=s" => \$dt 
) or usage("Error in command line arguments"); 
open my $fh, '<', $file or die $!; 

my $last=0; 
my $lastRotation=0; 
while(not eof($fh)) { 
    my $line = <$fh>; 
    $line =~ s/\R//g; #remove newlines 
    # 1:  4442084  369475664 [C 
    my ($instances,$size,$class) = ($line =~ /^\s*\d+:\s+(\d+)\s+(\d+)\s+([\$\[\w\.]+)\s*$/) ; 
    if($instances) { 
     print "$dt,$class,$instances,$size\n"; 
    } 
} 

close($fh); 

Crear una mesa para poner los datos en

CREATE TABLE heap_histogram (
    histwhen timestamp without time zone NOT NULL, 
    class character varying NOT NULL, 
    instances integer NOT NULL, 
    bytes integer NOT NULL 
); 

copiar los datos a la nueva tabla de

\COPY heap_histogram FROM 'heap.csv' WITH DELIMITER ',' CSV ; 

Ejecutar la consulta de decantación frente al tamaño (nº de bytes) consulta:

SELECT class, REGR_SLOPE(bytes,extract(epoch from histwhen)) as slope 
    FROM public.heap_histogram 
    GROUP BY class 
    HAVING REGR_SLOPE(bytes,extract(epoch from histwhen)) > 0 
    ORDER BY slope DESC 
    ; 

Interprete los resultados:

  class    |  slope   
---------------------------+---------------------- 
java.util.ArrayList  |  71.7993806279174 
java.util.HashMap   |  49.0324576155785 
java.lang.String   |  31.7770770326123 
joe.schmoe.BusinessObject |  23.2036817108056 
java.lang.ThreadLocal  |  20.9013528767851 

La pendiente es de bytes añadidos por segundo (ya que la unidad de época está en segundos). Si usa instancias en lugar de tamaño, entonces ese es el número de instancias agregadas por segundo.

Mi una de las líneas de código que crean este joe.schmoe.BusinessObject fue responsable de la pérdida de memoria. Estaba creando el objeto, anexándolo a una matriz sin verificar si ya existía. Los otros objetos también se crearon junto con BusinessObject cerca del código de fuga.

+0

Esto es más o menos exactamente lo que estaba haciendo. –

+0

¡Sí! Mi objetivo era proporcionar todos los detalles faltantes de tu publicación. – joseph

Cuestiones relacionadas