forzar example espaƱol como collector java garbage-collection

java - example - Ajuste de colecciones de basura para baja latencia



garbage collector java example (3)

Estoy buscando argumentos sobre cómo dimensionar mejor a la generación joven (con respecto a la generación anterior) en un entorno donde la baja latencia es crítica.

Mis propias pruebas tienden a mostrar que la latencia es más baja cuando la generación joven es bastante grande (por ejemplo, -XX: NewRatio <3), sin embargo, no puedo conciliar esto con la intuición de que cuanto mayor sea la generación joven, más tiempo llevará basura. recoger.

La aplicación se ejecuta en linux 64 bits, jdk 6.

El uso de la memoria es de aproximadamente 50 megabytes de objetos de larga duración que se cargan al inicio (= caché de datos), y a partir de ahí solo se crean (muchos) objetos de vida muy corta (con una vida media <1 milisegundo).

Cierto ciclo de recolección de basura tarda más de 10 milisegundos en ejecutarse ... lo que se ve realmente desproporcionado en comparación con la latencia de la aplicación, que de nuevo es de unos pocos milisegundos al máximo.


¿Ya ha habilitado configuraciones de GC más relevantes, como seleccionar un algoritmo de colector de pausa baja concurrente?

En términos generales, las generaciones jóvenes, titulares y permanentes deben dimensionarse para que coincidan con el perfil de su aplicación. Si tiene muchos objetos efímeros pero el joven es demasiado pequeño, muchos objetos se mantendrán en propiedad, forzando colecciones importantes más frecuentes de toda la generación titular. Del mismo modo, si el joven es demasiado grande, entonces la tenencia es necesariamente menor, y puede obligar a frecuentes colecciones importantes de tenencia.

Hablando en términos prácticos, creo que descubrirá que el tiempo que se dedica a las colecciones menores frente a las principales se intercambia a medida que aumenta el tamaño de la generación joven, y es óptimo en algún momento.

Tal vez sea útil notar que en aplicaciones de servidor "sensibles" al rendimiento, he encontrado que es necesario reducir la generación joven, en general. Esto se debe a que dichas aplicaciones ya debieron haber sido perfiladas para zonas activas de asignación de memoria y optimizadas, por lo que están produciendo pocos objetos de corta duración. Esto a su vez significa que la generación joven está acaparando demasiado del montón.

Así que supongo que primero haría esa optimización, luego vería aparecer NewRatio más allá de 8, y ver la salida dada por -verbose: gc para ver cómo el tiempo de GC y GC total se intercambia y dónde es óptimo.


Al intentar aplicaciones en tiempo real con Java, el ajuste de recolección de basura es esencial, pero también hay otros aspectos en los que debe pensar (por ejemplo, el compilador JIT, los temporizadores, el enhebrado y el manejo de eventos asíncronos).

Dado que parece haber una demanda de Java en tiempo real, Sun proporciona una especificación de Java Real-Time System y tiene una implementación comercial disponible. Puede encontrar más información aquí .


Para una aplicación que genera mucha basura de corta vida y nada duradero, entonces un enfoque que puede funcionar es un gran apilamiento con casi toda su generación y casi todo lo que conserva y conserva todo lo que sobrevive a una colección YG más de una vez.

Por ejemplo (supongamos que tienes un jvm de 32 bits)

  • Montón 3072M (Xms y Xmn)
  • 128M tenured (es decir Xmn 2944m)
  • MaxTenuringThreshold = 1
  • SurvivorRatio = 190 (es decir, cada espacio de supervivencia es 1/192 del YG)
  • TargetSurvivorRatio = 90 (es decir, llene a los sobrevivientes tanto como sea posible)

Los parámetros exactos que usaría para esta configuración dependen de cuál sea el tamaño de estado estable de su conjunto de trabajo (es decir, cuánto está vivo en el momento de cada colección). El pensamiento aquí obviamente va en contra de las reglas normales de tamaño de montón, pero entonces no tienes una aplicación que se comporte de esa manera. La idea es que la aplicación es en su mayoría basura de corta duración y un poco de datos estáticos, así que configure el jvm para que los datos estáticos se conserven rápidamente y luego tenga un YG lo suficientemente grande para que no se recopile v a menudo minimizando así la frecuencia de las pausas. Tendrás que girar las perillas repetidas veces para ver qué tamaño es bueno para ti y cómo se equilibra con el tamaño de la pausa que obtienes por colección. Es posible que encuentre pausas YG más cortas pero más frecuentes, por ejemplo.

No dice cuánto tiempo se ejecuta su aplicación, pero el objetivo aquí es no tener colecciones permanentes durante la vida de la aplicación. Esto puede ser imposible, por supuesto, pero vale la pena apuntar.

Sin embargo, no es solo el problema de la colección lo que es importante en su caso, sino que es donde se asigna la memoria. El recopilador NUMA (solo compatible con el colector de rendimiento y activado con el conmutador UseNUMA) hace uso de la observación de que un objeto a menudo se usa exclusivamente por el hilo que lo creó y, por lo tanto, asigna memoria en consecuencia. No estoy seguro de en qué se basa en Linux, pero usa MPO (optimización de ubicación de memoria) en Solaris, algunos detalles en uno de los blogs de GC.

Como está utilizando 64bit jvm, asegúrese de estar utilizando CompressedOops también.

Teniendo en cuenta la tasa de asignación de objetos (posiblemente algún tipo de lib de ciencia) y la duración de la vida, entonces debería prestar cierta atención a la reutilización de objetos. Un ejemplo de una lib que hace esto es la javalution StackContext

Finalmente, vale la pena señalar que las pausas de GC no son las únicas pausas de STW, podría ejecutarse con la compilación de acceso temprano 6u21 que tiene algunas correcciones a los switches PrintGCApplicationStoppedTime e PrintGCApplicationConcurrentTime (que efectivamente imprimen tiempo en un punto de seguridad global y tiempo entre esos puntos de seguridad). Puede usar el indicador tracesafepointstatistics para tener una idea de qué es lo que hace que necesite un punto de seguridad (es decir, ningún subproceso está ejecutando código byte).