2012-03-16 22 views
11

Estoy probando una API, escrita en Java, que se espera que minimice la latencia en el procesamiento de mensajes recibidos a través de una red. Para lograr estos objetivos, estoy jugando con los diferentes recolectores de basura que están disponibles.¿El recolector de basura en serie de Java funciona mucho mejor que otros recolectores de basura?

Estoy tratando de cuatro técnicas diferentes, que utilizan los siguientes indicadores para controlar la recolección de basura:

1) de serie: -XX: + UseSerialGC

2) Paralelo: -XX: + UseParallelOldGC

3) concurrente: -XX: + UseConcMarkSweepGC

4) Concurrent/incremental: -XX: + UseConcMarkSweepGC -XX: + CMSIncrementalMode -XX: + CMSIncrementalPacing

Corrí cada técnica en el transcurso de cinco horas. Periódicamente utilicé la lista de GarbageCollectorMXBean proporcionada por ManagementFactory.getGarbageCollectorMXBeans() para recuperar el tiempo total dedicado a la recolección de basura.

¿Mis resultados? Tenga en cuenta que la "latencia" aquí es "Cantidad de tiempo que mi aplicación + API pasó procesando cada mensaje extraído de la red".

de serie: 789 eventos de recolección por un total de 1309 m; latencia media nos 47.45, la mediana de latencia nos 8.704, 1.197 latencia máximo nos

paralelo: 1715 eventos de recolección por un total de 122518 ms; latencia media nos 450.8, la mediana de latencia nos 8.448, latencia máximo 8292 nos

concurrente: 4629 eventos de recolección por un total de 116229 ms; latencia media nos 707.2, la latencia media 9,216 nosotros, latencia máximo 9151 nos

incremental: 5066 eventos de recolección por un total de 200213 ms; latencia media 515.9 us, latencia mediana 9.472 us, latencia máxima 14209 us

Encuentro que estos resultados son tan improbables que rayan en el absurdo. ¿Alguien sabe por qué podría tener este tipo de resultados?

Ah, y para que conste, estoy usando Java HotSpot (TM) de 64 bits del servidor VM.

+0

¿Estás asumiendo que ejecutar dos cosas en paralelo es necesariamente más rápido que ejecutar una cosa tras otra? – aioobe

+0

Espero que la latencia máxima aumente aunque – jcoder

+0

Entonces, ¿cuántos mensajes se procesaron realmente en esas 5 horas en sus diferentes escenarios? ¿Estás ejecutando un hilo único o multiproceso? – pap

Respuesta

18

Estoy trabajando en una aplicación Java que se espera para maximizar el rendimiento y minimizar la latencia

dos problemas con eso:

  • Esos son a menudo objetivos contradictorios, por lo que necesita decidir cómo importante es cada uno contra el otro (le sacrificar 10% latencia para obtener el 20% de ganancia de rendimiento, o viceversa? ¿Su objetivo específico para algunos alquitrán de latencia obtener, más allá del cual no importa si es más rápido? Cosas por el estilo.)
  • Tu no han dado ningún resultado, ya sea en torno de éstos

Todo lo que se muestra es la cantidad de tiempo que se gasta en el recolector de basura.Si realmente logra más rendimiento, probablemente espere para ver más tiempo pasado en el recolector de basura. O para decirlo de otra manera, puedo hacer un cambio en el código para minimizar los valores de informes que estés muy fácilmente:

// Avoid generating any garbage 
Thread.sleep(10000000); 

Es necesario trabajar a cabo lo que es en realidad importante para usted. Mida todo lo que es importante, luego averigüe dónde se encuentra el intercambio. Por lo tanto, la primera tarea de es volver a ejecutar las pruebas y medir la latencia y el rendimiento. Puede también preocuparse por el uso total de la CPU (que no es lo mismo que la CPU en GC por supuesto), pero mientras no está midiendo sus objetivos principales, sus resultados no le proporcionan información particularmente útil.

+1

+1 Gran respuesta. Ojalá pudiera dar un +1 extra por su solución para evitar la generación de basura :-) – aioobe

+0

Tres cosas. Primero, entiendo que los objetivos son a menudo contradictorios. Supongo que "latencia" sería mi objetivo principal. En segundo lugar, no estoy simplemente iterando a través de un archivo o algo. Las aplicaciones procesan el tráfico de la red (el mismo conjunto de tráfico para cada ejecución de la aplicación), por lo que la cantidad de datos que se procesa es la misma en cada ejecución. En tercer lugar, publicaré mis resultados de latencia en mi publicación principal en un momento. – user1274193

+0

jaja. _Evitar generar cualquier basura_ .. simplemente genial! +1 – kromit

0

No se puede decir que un GC sea mejor que el otro. depende de sus requisitos y su aplicación.

pero si desea maximizar el rendimiento y minimizar la latencia: ¡GC es su enemigo! no debe llamar a GC en absoluto y también tratar de evitar que JVM llame a GC.

vaya con grupos de objetos de serie y de uso.

4

No me parece nada sorprendente.

El problema con la recolección de basura en serie es que, mientras se está ejecutando, no se puede ejecutar nada más (también conocido como "detiene el mundo"). Sin embargo, eso tiene un buen punto: mantiene la cantidad de trabajo gastada en la recolección de basura en casi su mínimo.

Casi cualquier tipo de recolección de basura paralela o simultánea tiene que hacer una cantidad considerable de trabajo extra para garantizar que todas las modificaciones en el montón aparezcan atómicas para el resto del código. En lugar de simplemente detener todo por un tiempo, tiene que detener solo esas cosas que dependen de un cambio en particular, y luego por el tiempo suficiente para llevar a cabo ese cambio específico. Luego permite que el código comience a ejecutarse nuevamente, llega al siguiente punto que va a realizar un cambio, detiene otros fragmentos de código que dependen de él, y así sucesivamente.

El otro punto (aunque en este caso, probablemente sea bastante menor) es que a medida que procesa más datos, generalmente espera generar más basura y, por lo tanto, dedicar más tiempo a la recolección de basura. Como el colector serial detiene todos los demás procesos mientras hace su trabajo, eso no solo hace que la recolección de basura sea rápida, sino que también evita que se genere más basura durante ese tiempo.

Ahora, ¿por qué digo que probablemente sea un colaborador menor en este caso? Eso es bastante simple: el colector serial solo consumió un poco más de un segundo de cada cinco horas. A pesar de que no se hizo nada más durante esos ~ 1.3 segundos, ese es un porcentaje tan pequeño de cinco horas que probablemente no haya una gran diferencia (si es que haya alguna) en su rendimiento general.

Resumen: el problema con la recolección de basura en serie no es que utilice un tiempo excesivo en general, sino que puede ser muy inconveniente si detiene el mundo justo cuando necesita una respuesta rápida. Al mismo tiempo, debo agregar que siempre que sus ciclos de recolección sean cortos, esto aún puede ser bastante mínimo. En teoría, las otras formas de GC limitan principalmente su peor caso, pero de hecho (por ejemplo, al limitar el tamaño del montón) a menudo también puede limitar su latencia máxima con un colector en serie.

2

Hubo una excelente charla de un ingeniero de Twitter en el 2012 QCon Conference sobre este tema - puede verlo here.

Se discutieron las diversas "generaciones" en la memoria JVM Hotspot y la recolección de basura (Eden, Survivor, Old). En particular, tenga en cuenta que el "Simultáneo" en ConcurrentMarkAndSweep solo se aplica a la generación anterior, es decir, los objetos que se quedan por un tiempo.

Los objetos de vida corta son GCd de la generación "Eden"; esto es barato, pero es un evento de GC "para el mundo", independientemente del algoritmo de GC que hayas elegido.

El consejo fue sintonizar a la generación joven primero, p. Asigne montones de Eden nuevos para que haya más posibilidades de que los objetos mueran jóvenes y sean reclamados a bajo precio. Use + PrintGCDetails, + PrintHeapAtGC, + PrintTenuringDistribution ... Si obtienes más del 100% de supervivientes, entonces no había espacio, por lo que los objetos pasan rápidamente a Old: esto es malo.

Al ajustar la generación anterior, si la latencia es la máxima prioridad, se recomienda probar ParallelOld con autoajuste primero (+ AdaptiveSizePolicy, etc.), luego pruebe con CMS, y luego con el nuevo G1GC.

+0

Las diapositivas también están disponibles en http://www.slideshare.net/aszegedi/everything-i-ever-learned-about-jvm-performance-tuning-twitter, si el enlace de arriba no está funcionando para usted. – ryenus

+0

Gracias: también actualicé el enlace en mi respuesta para señalar la nueva ubicación del video. – DNA

0

Con la colección en serie, solo sucede una cosa a la vez. Por ejemplo, incluso cuando hay varias CPU disponibles , solo se utiliza una para realizar la recopilación. Cuando se utiliza la recolección paralela, la tarea de recolección de basura se divide en partes y esas subpartes se ejecutan simultáneamente, en diferentes CPUs . La operación simultánea permite que la recopilación se realice más rápidamente, a expensas de , una cierta complejidad adicional y fragmentación potencial.

Mientras que el GC serie utiliza solo un hilo para procesar un GC, el GC paralelo utiliza varios hilos para procesar un GC, y por lo tanto, más rápido. Este GC es útil cuando hay suficiente memoria y una gran cantidad de núcleos. También se llama "GC de rendimiento".

Cuestiones relacionadas