java - oficial - tomcat animal
PĆ©rdida de memoria en una aplicaciĆ³n web Java (8)
Tengo una aplicación web Java que se ejecuta en Tomcat 7 que parece tener una pérdida de memoria. El uso promedio de memoria de la aplicación aumenta linealmente con el tiempo cuando se está bajo carga (determinado mediante JConsole). Una vez que el uso de la memoria llega a la meseta, el rendimiento se degrada significativamente. Los tiempos de respuesta van de ~ 100 ms a [300 ms, 2500 ms], por lo que esto realmente está causando problemas reales.
Perfil de memoria JConsole de mi aplicación:
Al usar VisualVM, veo que al menos la mitad de la memoria está siendo utilizada por matrices de caracteres (es decir, char []) y que la mayoría (aproximadamente el mismo número de cada una, 300,000 instancias) de las cadenas es una de las siguientes: "Falla de asignación" , "Copiar", "fin de GC menor", todo lo cual parece estar relacionado con la notificación de recolección de basura. Por lo que sé, la aplicación no supervisa el recolector de basura en absoluto. VisualVM no puede encontrar una raíz GC para ninguna de estas cadenas, por lo que me resulta difícil rastrear esto.
Analizador de memoria montón de volcado:
No puedo explicar por qué el uso de la memoria es así, pero tengo una teoría acerca de por qué el rendimiento se degrada una vez que lo hace. Si la memoria está fragmentada, la aplicación podría tardar mucho tiempo en asignar un bloque de memoria contiguo para manejar nuevas solicitudes.
Comparando esto con la aplicación de estado del servidor Tomcat, la memoria aumenta y se nivela en, pero no llega a un "piso" alto como mi aplicación. Tampoco tiene el alto número de caracteres inalcanzables [].
Perfil de memoria JConsole de la aplicación de estado del servidor Tomcat:
El volcado de pila del analizador de memoria de la aplicación de estado del servidor Tomcat:
¿Dónde podrían asignarse estas cadenas y por qué no se están recolectando basura? ¿Hay configuraciones de Tomcat o Java que podrían afectar esto? ¿Hay paquetes específicos que puedan afectar esto?
Acabo de encontrar el mismo problema en una aplicación totalmente diferente, por lo que probablemente Tomcat7 no tiene la culpa. El analizador de memoria muestra 10M instancias de cadena inalcanzables en el proceso (que se ha estado ejecutando durante aproximadamente 2 meses), y la mayoría de ellas tienen valores relacionados con la recolección de basura (por ejemplo, "Falla de asignación", "fin de GC menor")
Analizador de memoria
Full GC ahora se ejecuta cada 2 segundos, pero esas cadenas no se recopilan. Mi conjetura es que hemos golpeado un error en el código GC. Usamos la siguiente versión de java:
$ java -version
java version "1.7.0_06"
Java(TM) SE Runtime Environment (build 1.7.0_06-b24)
Java HotSpot(TM) 64-Bit Server VM (build 23.2-b09, mixed mode)
y los siguientes parámetros de VM:
-Xms256m -Xmx768m -server -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC
-XX:+UseParNewGC -XX:+CMSParallelRemarkEnabled -XX:NewSize=32m -XX:MaxNewSize=64m
-XX:SurvivorRatio=8 -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails
-Xloggc:/path/to/file
Como esto suena inespecífico, un candidato habría sido JSF. Pero entonces hubiera esperado que los mapas de hash fugas también.
Si usas JSF: en web.xml puedes probar:
- Cliente javax.faces.STATE_SAVING_METHOD
- com.sun.faces.numberOfViewsInSession 0
- com.sun.faces.numberOfLogicalViews 1
En cuanto a las herramientas: JavaMelody puede ser interesante para estadísticas continuas, pero necesita esfuerzo.
La meseta se debe a que la memoria disponible cae por debajo del umbral de porcentaje predeterminado que causa un GC total. Esto explica por qué el rendimiento disminuye ya que la JVM se detiene constantemente mientras intenta encontrar y liberar memoria.
Por lo general, aconsejo mirar los cachés de objetos, pero en su caso creo que el tamaño de su Heap es simplemente demasiado bajo para una instancia de Tomcat + webapp. Recomendaría aumentar su montón a 1G ( -Xms1024m -Xmx1024m
) y luego revisar su uso de memoria nuevamente.
Si aún ve el mismo tipo de comportamiento, debería tomar otro volcado de Heap y observar a los consumidores más grandes después de String y Char. Según mi experiencia, esto suele ser mecanismos de almacenamiento en caché. Aumente aún más su memoria o reduzca el almacenamiento en caché si es posible. Algunas memorias caché solo definen la cantidad de objetos, por lo que debe comprender qué tan grande es cada objeto almacenado en caché.
Una vez que entienda el uso de la memoria, puede reducirla nuevamente, pero IMHO 512MB sería un mínimo.
Actualizar:
No debe preocuparse por los objetos inalcanzables, ya que deberían ser limpiados por el GC. Además, es normal que los consumidores más grandes por tipo sean Cadena y Char: la mayoría de los objetos contendrán algún tipo de Cadena, por lo que tiene sentido que las Cadenas y caracteres sean los más comunes por frecuencia. Comprender qué contienen los objetos que contienen las cadenas es la clave para encontrar consumidores de memoria.
Por accidente, me topé con las siguientes líneas en nuestro archivo conf / catalina.properties de Tomcat que activan el almacenamiento en caché de cadenas. Esto podría estar relacionado con su caso si tiene alguno de ellos activado. Parece que otros están advirtiendo para utilizar la función.
tomcat.util.buf.StringCache.byte.enabled=true
#tomcat.util.buf.StringCache.char.enabled=true
#tomcat.util.buf.StringCache.trainThreshold=500000
#tomcat.util.buf.StringCache.cacheSize=5000
Puedo recomendar jvisualvm que viene junto con cada instalación de Java. Inicie el programa, conéctese a su aplicación web. Vaya a Monitor -> Heap Dump . Ahora puede tomar algún tiempo (dependiendo del tamaño). La navegación a través del Heap Dump es bastante fácil, pero el significado que tienes que descubrir por ti mismo (aunque no es demasiado complicado), por ejemplo
Vaya a Clases (dentro del heapdump), seleccione java.lang.String , haga clic derecho en Mostrar en la vista de instancias . Después de eso, verá en la tabla del lado izquierdo las instancias de String actualmente activas en su sistema. Haga clic en una instancia de String y verá algunos preferentes de String en la parte superior derecha de la tabla de la derecha, como el valor del String .
En la parte inferior derecha de la tabla derecha, verá desde dónde se hace referencia a esta instancia de String . Aquí tiene que comprobar desde dónde se hace referencia a la mayoría de sus * String * s. Pero con su caso (176/210, buena propensión para encontrar algunos ejemplos de cadenas que causan sus problemas pronto), debe quedar claro después de una inspección de dónde está el problema.
Yo sugeriría usar memoryAnalyzer para analizar su montón, ya que proporciona mucha más información.
http://www.eclipse.org/mat/ hay una aplicación independiente y eclipse incrustada. solo necesitas ejecutar jmap en tu aplicación y analizar el resultado con esto.
tomcat/bin/setenv.bat
la siguiente configuración de JMX de tomcat/bin/setenv.bat
:
set "JAVA_OPTS=%JAVA_OPTS%
-Dcom.sun.management.jmxremote=true
-Dcom.sun.management.jmxremote.port=9090
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"
Ya no puedo obtener volcados de memoria detallados, pero el perfil de memoria se ve mucho mejor:
24 horas después, el perfil de memoria se ve igual: