El programa Java se está volviendo más lento después de correr por un tiempo
linux performance (7)
Consideraría declarar las vars fuera del bucle para que la asignación de mem se haga una vez y elimine GC por completo.
Tengo un programa java que es un algoritmo de aprendizaje automático típico, que actualiza los valores de algunos parámetros mediante algunas ecuaciones:
for (int iter=0; iter<1000; iter++) {
// 1. Create many temporary variables and do some computations
// 2. Update the value for the parameters
}
Los cálculos de los parámetros de actualización son bastante complejos, y tengo que crear muchos objetos temporales, pero no se hace referencia a ellos fuera del bucle. El código en el bucle hace un uso intensivo de la CPU y no tiene acceso al disco. Este programa carga un conjunto de datos de entrenamiento relativamente grande, por lo tanto, le otorgé 10G de memoria (-Xmx10G) a JVM, que es mucho más grande de lo que requiere (máximo a ~ 6G por el comando "superior" o el administrador de tareas de la ventana).
Lo probé en varias máquinas Linux (centos 6, memoria 24G) y una máquina ventana (win7, 12G), ambas con SUN Hotspot JDK / JRE 1.8 instalado. No especifiqué otros parámetros de JVM excepto -Xmx. Ambas máquinas están dedicadas a mi programa.
En Windows, mi programa funciona bien: cada iteración usa un tiempo de ejecución muy similar. Sin embargo, el tiempo de ejecución en todas las máquinas centos es extraño. Inicialmente se ejecuta correctamente, pero se ralentiza drásticamente (~ 10 veces más lento) en la iteración séptima / octava, y luego disminuye la velocidad ~ 10% en cada iteración.
Sospecho que podría ser causado por el recolector de basura de Java. Por lo tanto, uso jconsole para monitorear mi programa. El GC menor ocurre con mucha frecuencia en ambas máquinas, porque el programa crea muchas variables temporales en el bucle. Además, usé el comando "jstat -gcutil $ pid $ 1s" y capturé las estadísticas:
Centos: https://www.dropbox.com/s/ioz7ai6i1h57eoo/jstat.png?dl=0
Ventana: https://www.dropbox.com/s/3uxb7ltbx9kpm9l/jstat-winpng.png?dl=0
[Editado] Sin embargo, las estadísticas en dos tipos de máquinas difieren mucho:
- "S1" en las ventanas salta rápido entre 0 y 50, mientras que permanece en "0.00" en centos.
- La "E" en las ventanas cambia muy rápidamente de 0 a 100. A medida que imprimo la estadística por cada segundo, la captura de pantalla no muestra su incremento a 100. Sin embargo, en centos, la "E" aumenta bastante lentamente hacia 100, y luego se reduce a 0, y aumenta de nuevo.
Parece que el comportamiento extraño de mi programa se debe a Java GC? Soy nuevo en el monitor de rendimiento de Java y no tengo una buena idea para optimizar la configuración de los parámetros del GC. ¿Tienes alguna sugerencia? ¡Muchas gracias!
Dar a Java (o cualquier lenguaje de recolección de basura) demasiada memoria tiene un efecto adverso en el rendimiento. Los objetos vivos (referenciados) se vuelven cada vez más escasos en la memoria, lo que da como resultado búsquedas más frecuentes de la memoria principal. Tenga en cuenta que en los ejemplos que nos ha mostrado, las ventanas más rápidas son las de GC más rápido y completo que Linux, pero los ciclos de GC (especialmente los gcs completos) suelen ser perjudiciales para el rendimiento.
Si la ejecución del conjunto de entrenamiento no lleva un tiempo excepcionalmente largo, intente realizar una evaluación comparativa en diferentes asignaciones de memoria.
Una solución más radical, pero que debería tener un gran impacto es eliminar (o reducir lo más posible) la creación de objetos dentro del bucle reciclando objetos en grupos.
En mi experiencia, JAVA necesita suficiente memoria y 2+ CPU. De lo contrario, el uso de la CPU será muy extenso cuando GC comience a ejecutarse.
Lamento publicar esto como una respuesta, pero no tengo suficiente puntuación para comentar.
Si cree que es un problema relacionado con GC, lo cambiaría para Garbage 1 Collector –XX: + UseG1GC
Encontré esta breve explicación al respecto: http://blog.takipi.com/garbage-collectors-serial-vs-parallel-vs-cms-vs-the-g1-and-whats-new-in-java-8/
¿Puede ejecutar su software bajo perfil? Intente utilizar el jprofiler, VisualVM o incluso el generador de perfiles netbeans. Te puede ayudar mucho.
Noté que tienes tu propia encapsulación de un vector y una matriz. Tal vez estés gastando más memoria de la necesaria con eso también. Pero no creo que ese sea el problema.
Lo siento de nuevo por no contribuir como un comentario. (Sería más apropiado)
Podría intentar llamar a System.gc () cada dos iteraciones para ver si el rendimiento aumenta o disminuye. Esto puede ayudarlo a limitarlo a algunos de los diagnósticos de respuestas anteriores.
Primero, es una buena práctica común declarar variables fuera de los bucles para evitar la colección de garbace. como dijo ''Wagner Tsuchiya'', intente ejecutar un perfilador si tiene dudas sobre el GC. Si quieres algunos consejos sobre el ajuste de GC, encontré buen blogpost .
Si el tiempo de GC es de cientos de milisegundos como se muestra en su captura de pantalla, entonces GC no es el problema aquí. Le sugiero que busque en la contención de bloqueo y posiblemente IO utilizando un generador de perfiles (Netbeans es genial). Sé que usted declaró que su programa hacía muy poco IO pero con la creación de perfiles (como la depuración) tiene que eliminar todos sus supuestos e ir paso a paso.