java - collection - Ajuste de rendimiento de JVM para aplicaciones grandes
usegcoverheadlimit (7)
Los parámetros de JVM predeterminados no son óptimos para ejecutar aplicaciones grandes. Cualquier información de personas que la han ajustado en una aplicación real sería útil. Estamos ejecutando la aplicación en una máquina de Windows de 32 bits, donde la JVM del cliente se usa de forma predeterminada . Hemos agregado -server y cambiado NewRatio a 1: 3 (Una generación joven más grande).
¿Algún otro parámetro / ajuste que haya probado y encontrado útil?
[Actualización] El tipo específico de aplicación de la que estoy hablando es una aplicación de servidor que raramente se apaga, tomando al menos -Xmx1024m. Supongamos también que la aplicación ya está perfilada. Estoy buscando pautas generales en términos de rendimiento de JVM solamente.
Esto dependerá en gran medida de su aplicación y del proveedor y la versión de JVM. Debe tener claro lo que considera que es un problema de rendimiento. ¿Le preocupan ciertas secciones críticas del código? ¿Ya has perfilado la aplicación? ¿La JVM está desperdiciando demasiado tiempo recolectando basura?
Probablemente comenzaría con la opción -verbose: gc JVM para ver cómo funciona la recogida de basura. Muchas veces, la solución más simple es simplemente aumentar el tamaño máximo de almacenamiento dinámico con -Xmx. Si aprende a interpretar la salida -verbose: gc, le dirá casi todo lo que necesita saber sobre cómo ajustar la JVM como un todo. Pero hacer esto solo no hará mágicamente que el código mal ajustado solo vaya más rápido. La mayoría de las opciones de ajuste de JVM están diseñadas para mejorar el rendimiento del recolector de basura y / o los tamaños de memoria.
Para crear perfiles, me gusta yourkit.com
Mire aquí (o haga una búsqueda en Google para la sintonización del punto de acceso) http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html
Definitivamente desea perfilar su aplicación antes de intentar sintonizar la vm. NetBeans tiene un buen generador de perfiles integrado que te permitirá ver todo tipo de cosas.
Una vez, alguien me dijo que el GC estaba roto para su aplicación: miré el código y descubrí que nunca cerraron ninguno de los resultados de la consulta de la base de datos, por lo que retuvieron cantidades masivas de matrices de bytes. Una vez que cerramos los resultados, el tiempo pasó de más de 20 minutos y un GB de memoria a aproximadamente 2 minutos y una cantidad muy pequeña de memoria. Pudieron eliminar los parámetros de ajuste de JVM y las cosas fueron felices.
Sugiero que perfile su aplicación con el muestreo de la CPU y la supervisión de la asignación de objetos activados al mismo tiempo. Encontrarás resultados muy diferentes que pueden ser útiles para ajustar tu código. También intente utilizar el generador de perfiles incorporado de hprof, también puede proporcionar resultados muy diferentes.
En general, crear perfiles de su aplicación hace mucha más diferencia que los argumentos de JVM.
La mejor forma de responder a esto es realizar pruebas controladas en la aplicación lo más cerca posible de un entorno de ''producción''. Es muy posible que el uso de -server, un tamaño de pila inicial razonable y el comportamiento relativamente inteligente de las JVM recientes se comporten tan bien o mejor que la gran mayoría de las configuraciones que uno normalmente intentaría.
Hay una excepción específica a esta amplia generalización: en el caso de que se esté ejecutando en un contenedor web, hay muchas posibilidades de que desee aumentar la configuración de generación permanente.
Java en la máquina de Windows de 32 bits, sus opciones son limitadas. En mi experiencia, la configuración del siguiente parámetro afectará el rendimiento de la aplicación:
- tamaños de memoria
- elección de colectores GC
- parámetros relacionados con los colectores GC
Hay una gran cantidad de esa información alrededor.
Primero, perfila el código antes de sintonizar la JVM.
En segundo lugar, lea la documentación de JVM con cuidado; hay muchas "leyendas urbanas" alrededor. Por ejemplo, el indicador -server solo ayuda si la JVM se mantiene residente y se ejecuta durante un tiempo; -server "enciende" el JIT / HotSpot, y eso necesita tener muchos pases a través de la misma ruta para aparecer. -server, por otro lado, ralentiza la ejecución inicial de la JVM, ya que hay más tiempo de configuración.
Hay varios buenos libros y sitios web alrededor. Ver, por ejemplo, http://www.javaperformancetuning.com/
Prefacio
Fondo
Estado en una tienda de Java. Pasé meses enteros dedicados a ejecutar pruebas de rendimiento en sistemas distribuidos, las aplicaciones principales están en Java. Algunos de los cuales implican productos desarrollados y vendidos por Sun (y luego Oracle).
Repasare las lecciones que aprendí, un poco de historia sobre la JVM, algunas charlas sobre el funcionamiento interno, un par de parámetros explicados y finalmente algunos ajustes. Tratando de mantenerlo al punto para que pueda aplicarlo en la práctica.
Las cosas están cambiando rápidamente en el mundo de Java, por lo que parte de él podría estar ya desactualizado desde el año pasado en que hice todo eso. (¿Ya está Java 10?)
Buenas practicas
Lo que DEBERÍA hacer: punto de referencia, Benchmark, BENCHMARK!
Cuando realmente necesita saber acerca de las actuaciones, necesita realizar puntos de referencia reales, específicos para su carga de trabajo. No hay alternativas
Además, debe monitorear la JVM. Habilita la supervisión. Las buenas aplicaciones generalmente proporcionan una página web de monitoreo y / o una API. De lo contrario, existe la herramienta Java común (JVisualVM, JMX, hprof y algunas banderas JVM).
Tenga en cuenta que generalmente no hay rendimiento que ganar ajustando la JVM . Es más un "estrellarse o no chocar, encontrar el punto de transición" . Se trata de saber que cuando le das esa cantidad de recursos a tu aplicación, siempre puedes esperar esa cantidad de actuaciones a cambio. El conocimiento es poder.
Las actuaciones son dictadas principalmente por su aplicación. Si quieres más rápido, tienes que escribir un código mejor.
Lo que HARÁS la mayoría del tiempo: vivir con valores predeterminados confidenciales confiables
No tenemos tiempo para optimizar y sintonizar cada aplicación que hay. La mayoría de las veces simplemente viviremos con valores predeterminados razonables.
Lo primero que debe hacer al configurar una nueva aplicación es leer la documentación. La mayoría de las aplicaciones serias incluyen una guía para la optimización del rendimiento, que incluye consejos sobre la configuración de JVM.
Luego puede configurar la aplicación: JAVA_OPTS: -server -Xms???g -Xmx???g
-
-server
: habilitar optimizaciones completas (esta bandera es automática en la mayoría de JVM hoy en día) -
-Xms
-Xmx
: establece el montón mínimo y máximo (siempre el mismo valor para ambos, se trata de las únicas optimizaciones para hacer).
Bien hecho, usted conoce todos los parámetros de optimización que hay que conocer sobre la JVM, ¡enhorabuena! Eso fue simple: D
Lo que NO DEBES hacer, NUNCA:
No copie la secuencia aleatoria que encontró en Internet, especialmente cuando toman varias líneas como esta:
-server -Xms1g -Xmx1g -XX:PermSize=1g -XX:MaxPermSize=256m -Xmn256m -Xss64k -XX:SurvivorRatio=30 -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=10 -XX:+ScavengeBeforeFullGC -XX:+CMSScavengeBeforeRemark -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Dsun.net.inetaddr.ttl=5 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=`date`.hprof -Dcom.sun.management.jmxremote.port=5616 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -server -Xms2g -Xmx2g -XX:MaxPermSize=256m -XX:NewRatio=1 -XX:+UseConcMarkSweepGC
Por ejemplo, esto que se encuentra en la primera página de google es completamente terrible. Hay argumentos especificados múltiplos veces con valores conflictivos. Algunos simplemente fuerzan los valores predeterminados de JVM (finalmente los valores predeterminados de 2 versiones de JVM). Algunos son obsoletos y simplemente ignorados. Y finalmente, al menos un parámetro es tan inválido que bloqueará constantemente la JVM al inicio por su mera existencia.
Ajuste real
¿Cómo eliges el tamaño de la memoria?
Lea la guía de su aplicación, debería dar alguna indicación. Controle la producción y ajuste luego. Realice algunos puntos de referencia si necesita precisión.
Nota importante : el proceso de Java llevará hasta un máximo de MÁXIMO 10% . La sobrecarga del X% es la gestión del montón, no incluida en el montón.
Toda la memoria suele ser preasignada por el proceso al inicio. Puede ver el proceso usando el máximo de pila TODO EL TIEMPO. Simplemente no es verdad. Debe usar las herramientas de monitoreo de Java para ver lo que realmente se está usando.
Encontrar el tamaño correcto:
- Si se bloquea con OutOfMemoryException, no es suficiente memoria
- Si no falla con OutOfMemoryException, es demasiada memoria
- Si es demasiada memoria PERO el hardware lo consiguió y / o ya está pagado, ¡es el número perfecto , trabajo hecho!
JVM6 es bronce, JVM7 es oro, JVM8 es platino ...
La JVM está mejorando para siempre. Garbage Collection es algo muy complejo y hay muchas personas muy inteligentes trabajando en ello. Tuvo enormes mejoras en la última década y seguirá haciéndolo.
Para fines informativos. Hay al menos 4 recolectores de basura disponibles en Oracle Java 7-8 (HotSpot) y OpenJDK 7-8. (Otras JVM pueden ser completamente diferentes, por ejemplo, Android, IBM, incrustadas):
- SerialGC
- ParallelGC
- ConcurrentMarkSweepGC
- G1GC
- (más variantes y configuraciones)
[A partir de Java 7 y en adelante. El código de Oracle y OpenJDK se comparten parcialmente. El GC debería ser (en su mayoría) el mismo en ambas plataformas.]
JVM> = 7 tiene muchas optimizaciones y elige valores predeterminados decentes. Cambia un poco por plataforma. Equilibra muchas cosas. Por ejemplo, decidir habilitar optimizaciones multinúcleo o no, si la CPU tiene múltiples núcleos. Deberías dejar que lo haga. No cambie ni fuerce la configuración del GC.
Está bien dejar que la computadora tome la decisión por usted (para eso están las computadoras). Es mejor tener la configuración JVM que sea 95% -optimal todo el tiempo que forzar una "colección siempre agresiva de 8 núcleos para tiempos de pausa más bajos" en todas las casillas, la mitad de ellas son t2.small al final.
Excepción : cuando la aplicación viene con una guía de rendimiento y ajustes específicos en su lugar. Está perfectamente bien dejar la configuración proporcionada tal como está.
Consejo : Pasar a una JVM más nueva para beneficiarse de las últimas mejoras a veces puede proporcionar un buen impulso sin mucho esfuerzo.
Caso especial: -XX: + UseCompressedOops
La JVM tiene una configuración especial que obliga a usar internamente el índice de 32 bits (léase: similar a un puntero). Eso permite direccionar 4 294 967 295 objetos * 8 bytes dirección => 32 GB de memoria. (NO debe confundirse con el espacio de direcciones de 4 GB para punteros REALES).
Reduce el consumo general de memoria con un posible impacto positivo en todos los niveles de almacenamiento en caché.
Ejemplo de vida real : la documentación de ElasticSearch indica que un nodo de ejecución de 32 GB y 32 bits puede ser equivalente a un nodo de 64 GB de 40 GB en términos de datos reales guardados en la memoria.
Una nota sobre la historia : se sabía que la bandera era inestable en la era pre-java-7 (tal vez incluso pre-java-6). Ha funcionado perfectamente en JVM más reciente por un tiempo.
Mejoras en el rendimiento de la máquina virtual Java HotSpot ™
[...] En Java SE 7, el uso de oops comprimidos es el predeterminado para los procesos de JVM de 64 bits cuando -Xmx no se especifica y para valores de -Xmx menores que 32 gigabytes. Para JDK 6 antes de la versión 6u23, use el indicador -XX: + UseCompressedOops con el comando java para habilitar la característica.
Ver : una vez más, la JVM está a años luz de la puesta a punto manual. Aún así, es interesante saber sobre él =)
Caso especial: -XX: + UseNUMA
El acceso a la memoria no uniforme (NUMA) es un diseño de memoria de la computadora utilizado en el multiprocesamiento, el tiempo de acceso a la memoria depende de la ubicación de la memoria en relación con el procesador. Fuente: Wikipedia
Los sistemas modernos tienen arquitecturas de memoria extremadamente complejas con múltiples capas de memoria y cachés, ya sean privados o compartidos, entre núcleos y CPU.
Obviamente, acceder a los datos en la memoria caché L2 en el procesador actual es MUCHO más rápido que tener que recorrer todo el camino hasta una memoria extraíble desde otro socket.
Creo que todos los sistemas de enchufes múltiples vendidos hoy en día son NUMA por diseño, mientras que todos los sistemas de consumidores NO lo son. Compruebe si su servidor admite NUMA con el comando numactl --show
on linux.
El indicador con reconocimiento de NUMA le dice a la JVM que optimice las asignaciones de memoria para la topología de hardware subyacente.
El aumento del rendimiento puede ser sustancial (es decir, dos dígitos: + XX%). De hecho, alguien que cambie de un "NO-NUMA 10CPU 100GB" a un "NUMA 40CPU 400GB" podría experimentar una pérdida [dramática] en las actuaciones si no conoce la bandera.
Nota : hay discusiones para detectar NUMA y establecer el indicador automáticamente en la JVM http://openjdk.java.net/jeps/163
Bonificación : es necesario optimizar todas las aplicaciones que pretenden ejecutarse en hardware grande (es decir, NUMA). No es específico para aplicaciones Java.
Hacia el futuro: -XX: + UseG1GC
La última mejora en Garbage Collection es el recopilador G1 (léase: Garbage First) .
Está diseñado para núcleos altos, sistemas de memoria alta. En el mínimo absoluto 4 núcleos + 6 GB de memoria. Está dirigido a bases de datos y aplicaciones de uso intensivo de memoria que usan 10 veces esa cantidad y más.
Versión corta, en estos tamaños, el GC tradicional enfrenta demasiados datos para procesar a la vez y las pausas se están saliendo de control. El G1 divide el montón en muchas secciones pequeñas que se pueden gestionar de forma independiente y en paralelo mientras se ejecuta la aplicación.
La primera versión estaba disponible en 2013. Ya es lo suficientemente madura como para producción, pero no estará disponible por defecto pronto. Eso vale la pena intentarlo para grandes aplicaciones.
No tocar: Tamaños de generación (NewGen, PermGen ...)
El GC divide la memoria en varias secciones. (Sin entrar en detalles, puede googlear "Java GC Generations").
La última vez que he pasado una semana probando 20 banderas diferentes de generaciones combinadas en una aplicación con 10000 hits / s. Recibí un impulso excelente que iba del -1% al + 1%.
Las generaciones de GC de Java son un tema interesante para leer artículos sobre los que escribir. No son una cosa que sintonizar a menos que seas parte del 1% que puede dedicar un tiempo considerable para obtener ganancias insignificantes en el 1% de las personas que realmente necesitan optimizaciones.
Conclusión
Espero que esto le pueda ayudar. Diviértete con la JVM.
¡Java es el mejor lenguaje y la mejor plataforma del mundo! Ve a difundir el amor: D