2011-01-21 21 views
6

Me he dado cuenta de que al reproducir audio en java, el escenario MarkSweepCompact en gc es demasiado largo y da como resultado cortos períodos de silencio, lo cual es inaceptable. Entonces necesito usar una pausa baja en gc. He intentado Parallel y CMS, parecen funcionar mejor porque supongo que la pausa es más corta y no hacen la recopilación completa tan a menudo como la predeterminada.Tuning GC para la aplicación de audio Java

Hasta ahora hemos probado mi programa con las siguientes opciones para ParallelGC:

-XX:+UseParallelGC 
-XX:MaxGCPauseMillis=70 

y para ConcurrentMarkSweep:

-XX:+UseConcMarkSweepGC 
-XX:+CMSIncrementalMode 
-XX:+CMSIncrementalPacing 

También probé G1GC, pero todavía es experimental en Java 6. Opciones para ambos modos:

-Xms15m 
-Xmx40m 
-XX:+UnlockExperimentalVMOptions 
-XX:+CMSClassUnloadingEnabled 
-XX:+TieredCompilation 
-XX:+AggressiveOpts 
-XX:+UseAdaptiveSizePolicy 
-Dsun.java2d.noddraw=false 
-Dswing.aatext=true 
-XX:MaxPermSize=25m 
-XX:MaxHeapFreeRatio=10 
-XX:MinHeapFreeRatio=10 

¿Qué GC es mejor en esta situación? ¿Se puede optimizar cualquiera de estas configuraciones para obtener el mejor rendimiento de la CPU y un uso mínimo de la memoria también?

EDITAR Para reconocer la pausa que grabo tiempo para escribir datos de audio en la línea de salida, por lo general es entre 92 a 120 ms (estoy escribiendo 16384 bytes = ~ 92ms), anuncio cuando se ejecuta completa GC, es más de 200 ms:

65.424: [Full GC (System) [PSYoungGen: 872K->0K(2432K)] [PSOldGen: 12475K->12905K(16960K)] 13348K->12905K(19392K) [PSPermGen: 15051K->15051K(22272K)], 0.2145081 secs] [Times: user=0.20 sys=0.00, real=0.21 secs] 
Was writing 16384 bytes, time to write 263 ms 

Edit2 patrón de asignación para mi aplicación es el siguiente: se carga montón de objetos en el inicio, a continuación, se inicia la reproducción y supongo que la mayoría de los objetos después de que se asignan por la interfaz gráfica de usuario, porque mirar/pausar el audio no cambia mucho el gráfico de GC. Esto es lo que visualgc muestra con gc paralelo: alt text

El gráfico comienza en el inicio y comienzo la reproducción. Marcados se

1) de retardo de sonido y GC completa, creo que aumentó el tamaño antigua:

101.646: [Full GC [PSYoungGen: 64K->0K(6848K)] [PSOldGen: 15792K->12773K(19328K)] 15856K->12773K(26176K) [PSPermGen: 15042K->14898K(23808K)], 0.2411479 secs] [Times: user=0.19 sys=0.00, real=0.24 secs] 

2) Abro la ventana de la aplicación y la pausa de reproducción. Nada realmente cambia, un poco más tarde aumenta el tamaño del edredón.

3) Abro las ventanas y comienzo de nuevo la reproducción.

¿Debo aumentar el tamaño de la antigua generación? ¿Cómo puedo hacer eso? Estoy corriendo con -XX: NewRatio = 10 y -XX: NewSize = 10m

Gracias.

Respuesta

5

El registro que proporciona es demasiado pequeño para proporcionar un análisis real pero dice que gastó 200ms haciendo poco, ya que la vieja generación está básicamente llena. Esto significa que tu montón es demasiado pequeño o tienes una pérdida de memoria. No hay mucho que pueda hacer para sintonizar el algoritmo GC en esta situación. Por lo tanto, el resto de esta respuesta se trata de cómo puede obtener más información de la aplicación y/o cómo ajustar GC una vez que haya eliminado la pérdida de memoria o tenga un montón más grande.

En gran medida, pausa baja significa hacer todo lo posible para mantener las colecciones como colecciones jóvenes solamente.

Realmente necesita iniciar sesión exactamente cuando comienza y termina de escribir y correlacionar eso con las pausas STW que ocurren en la JVM durante ese período; de lo contrario, no tiene idea de qué podría estar causando el problema o qué tan grave es.

Cosas que haría de inmediato;

  1. cambio de su registro para que la salida de una sola línea fácilmente analizable por un guión (quizás starttime, tiempo final, duración)
  2. añadir el PrintGCApplicationStoppedTime y PrintGCApplicationConcurrentTime interruptores de manera que se obtiene un registro de cada STW pausa y no sólo los eventos de recolección
  3. utilizan la última JVM (es decir 6u23) ya que ha habido una gran cantidad de mejoras para hotspot en el último año o dos, por lo que hay puntos utilizando una más antigua
  4. no se dice si' Re memoria limitada, pero definitivamente aumentaría se el tamaño del montón si puedes, 40M es bastante pequeño para que no tengas mucho espacio para jugar con
  5. Ejecuta la aplicación con visualgc conectado, te da una visión más completa de lo que está pasando IMO ya que tienes todo el diferentes vistas al mismo tiempo

La clave es determinar dónde se está quedando sin espacio y por qué. La respuesta a eso probablemente radica en cómo es el patrón de asignación de tu aplicación, ¿está generando una carga de objetos de corta vida, de modo que estás quemando tu pequeño eden realmente rápido? ¿El límite de tenencia es demasiado alto como para que pongas ping pong objetos a través de los espacios de sobrevivientes antes de que se consoliden de todos modos y forzando gcs de tenencia frecuente (lento)?

Algunas otras cosas a tener en cuenta ...

  • iCMS (incremental) fue diseñado para su uso en máquinas de 1 o 2 núcleos, ¿eso describe su máquina? ¿Cuántos núcleos tienes? puede que sólo quieren dejar caer esa opción
  • CMS tiene una sola fase roscado (marca init), esto podría estar dañando que
  • CMS normalmente prefiere un montón más grande que otros colectores, el suyo es bastante pequeña

Editar después del gráfico visualgc agregado a la pregunta Dado que su memoria está limitada, necesita hacer un mejor uso del espacio que tiene, la única manera de hacerlo será a través de la evaluación comparativa repetida ... idealmente con una repetible prueba.

  • puede usar -Xmn para especificar establecer el tamaño de la generación joven, el resto se le dará a la tenencia.
  • es posible que desee ajustar su uso de los espacios de supervivencia para que dejar que se completa antes de que se intercambian y se alquilan objetos viven allí por más tiempo antes de que lleguen titulares
    • -XX:TargetSurvivorRatio=90 conjuntos ésta así lo que necesita un espacio sobreviviente para estar 90% lleno antes de que se copie, obviamente hay una compensación aquí entre el costo de copiar y usar el espacio
    • use -XX:+PrintTenuringDistribution para mostrar el tamaño de cada espacio y cómo son las cosas, también puede ver esto en visualgc
    • usa -XX:+MaxTenuringThreshold para especificar cuántas veces un objeto puede sobrevivir a una colección joven (se copia de 1 espacio de superviviente a otro) bef el mineral está en posesión, p. si sabe que sólo recibe la basura de corta duración o materia que vive para siempre, entonces este valor está a 1 es sensata
  • es necesario entender lo que está provocando las colecciones permanentes y podría considerar la adopción de medidas para que esto desencadena después
    • para CMS esto puede implicar ajustar -XX:CMSInitiatingOccupancyFraction=<value>, por ejemplo se establece en 80 y activará CMS al 80% de ocupación de titular (NB: es una mala idea equivocarse, por lo que puede preferir que el punto de acceso administre esto, configurarlo demasiado pequeño y se acumula demasiado a menudo matando el rendimiento, configúrelo). demasiado grande y puede desencadenar demasiado tarde causando una colección completa no programada con tiempo de pausa correspondientemente larga
  • si realmente es colecciones antiguas que están sufriendo y que necesita bajo pausa a continuación, utilizar la CMS y ParNew

Finalmente, obtenga un generador de perfiles y averigüe de dónde viene la basura, puede que le resulte más fácil controlar la velocidad a la que se genera la basura y luego inyectarle esfuerzo en el agujero negro que puede ser el ajuste de la GC.

+0

Sí, tengo memoria limitada, es un reproductor de audio, y no debería usar más de 30-40 Mb. En general, mi montón está medio lleno. –

+0

visualgc es una gran herramienta, gracias, he actualizado la pregunta con el gráfico visualgc. –

3

Esto implica que se están promoviendo demasiados objetos objetos fuera del espacio de edredón ya que el GC principal no debería tener que ocuparse demasiado. Puede aumentar la proporción de espacio otorgado a las nuevas generaciones con -XX: NewRatio. Pruebe 10 y suba.
Mejor aún, investigue cómo puede reducir el alcance sobre el que los objetos en su programa siguen siendo referenciados.

1

Ok, en resumen, tiene un requisito no funcional en un sistema que no está especificado para cumplir con este requisito. La respuesta "correcta" sería usar una implementación de JVM capaz en tiempo real. Pero la mayoría son caros, y supongo que aceptaría una solución correcta al 99,9% por ciento.

Lo primero que debes hacer es encontrar una forma de medir estas interrupciones. De lo contrario, cualquier experimento para comparar diferentes recolectores de basura está condenado a ser poco confiable.

Después de la introducción de esta declaración, vamos a llegar a su problema:

Dijiste que el recolector de basura es la introducción de pausas en el juego de vuelta de sonido. Sus opciones son:

  1. Mejore el recolector de basura utilizando las opciones más adecuadas.
  2. Genera menos basura.
  3. Llame periódicamente al recolector de basura, pero esto puede muy bien conducir al efecto contrario. ¡Tienes que medir!
  4. Utilice técnicas de ocultación de latencia para reducir la influencia de las pausas inducidas por el recolector de basura.

Para concluir: si realmente desea deshacerse de ese problema, (1) encuentre una manera de medirlo, (2) haga experimentos, (3) encuentre la causa raíz, (4) resuelva el problema causa raíz, y (5) medir que realmente lo has resuelto.

+0

estoy usando jconsole para monitorear el gc, y estoy experimentando ahora mismo. –

+0

puede grabar el sonido y los gráficos en visualvm o jconsole para saber qué está sucediendo en el momento en que hay vacíos en la salida de sonido? – jmg

+0

en lugar de grabar el sonido, yo registro el tiempo que toma escribir audio en la línea de salida, vea mi edición. –

1

Sé que esto es una cuestión de edad y el PO, probablemente, no es ni siquiera interesa más, pero me molesta que estas líneas eran en su configuración:

-XX:MaxHeapFreeRatio=10 
-XX:MinHeapFreeRatio=10 

Para mí esto significa que su máquina virtual intentará para CONSTANTEMENTE solicitar memoria del sistema o liberarla - estoy bastante seguro de que tiene que haber una brecha entre estos dos números.

Además, para cualquier otra persona que intente hacer un sistema Java en tiempo real, el truco es asignar TODOS sus objetos por adelantado y luego nunca asignar nada más.

Puede ser difícil, pero no es imposible por una posibilidad remota: encienda -verbose: gc y simplemente elimine "Nuevos" y otras cosas que asignan memoria hasta que no vea ningún gcs en absoluto.

En una GUI, por cierto, esto significa pre-crear todos los elementos de su GUI y nunca liberarlos, solo ocultarlos y mostrarlos. También significa que no hay manipulación de cadenas (use StringBuffers y constantes de cadena solamente - esto es lo más difícil de resolver porque muchas llamadas al sistema dependen de cadenas)

+0

Hola. Sí, de hecho abandoné ese proyecto, pero gracias por tu respuesta. Establecí esos dos parámetros para minimizar la huella de memoria de la aplicación. Supongo que al establecerlos en el mismo valor puede cambiar el tamaño del montón en cada gc, lo que aumenta el tiempo de gc. Actualmente lo configuro a 20/10. Y todavía acelera la memoria :( –

+0

Y lo que hice al final fue minimizar la cantidad de objetos vivientes largos y hacer que los espacios para jóvenes y de supervivencia fueran más grandes para que la menor cantidad posible de objetos llegara a la generación anterior. –

+0

Sí, java parece encantar la memoria ... Me encontré con esto en busca de una forma de minimizar la huella en lugar de pausas ... Tengo un solo tomcat de 10 gb que hace que mi máquina de desarrollo sea seriamente infeliz. Una cosa que he notado, cuando Lo configuré a 10/20, no ayudó tanto como cuando lo configuré en 20/40, creo que hay un límite inferior después del cual comienza ignorando los nubmers (Silenciosamente, por supuesto). –