yugioh recolector recoleccion qué garbagecollector español ejemplo ejecutar definicion collector basura activar java memory-leaks garbage-collection jvm

java - recoleccion - Hemorragia masiva de memoria que hace que el tamaño del montón pase de unos 64 mb a 1,5 gb en aproximadamente 8 segundos. ¿Problema con el recolector de basura?



recolector de basura java ejemplo (2)

Aquí está el problema:

Como puede ver, ¡el uso de la memoria se sale de control! Tuve que agregar argumentos a la JVM para aumentar el tamaño máximo para evitar errores de falta de memoria mientras descubro lo que está sucediendo. ¡No está bien!

Resumen de la aplicación básica (para el contexto)

Esta aplicación se usará (eventualmente) para operaciones básicas de CV y ​​concordancia de plantilla con fines de automatización. Quiero alcanzar la mayor velocidad posible de cuadros para ver la pantalla y manejar todo el procesamiento a través de una serie de hilos de consumo separados.

Rápidamente descubrí que la clase de Robot original es realmente terrible en cuanto a la velocidad, así que abrí la fuente, saqué todo el esfuerzo duplicado y desperdicié, y lo reconstruí como mi propia clase llamada FastRobot.

El Código de la clase:

public class FastRobot { private Rectangle screenRect; private GraphicsDevice screen; private final Toolkit toolkit; private final Robot elRoboto; private final RobotPeer peer; private final Point gdloc; private final DirectColorModel screenCapCM; private final int[] bandmasks; public FastRobot() throws HeadlessException, AWTException { this.screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); this.screen = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); toolkit = Toolkit.getDefaultToolkit(); elRoboto = new Robot(); peer = ((ComponentFactory)toolkit).createRobot(elRoboto, screen); gdloc = screen.getDefaultConfiguration().getBounds().getLocation(); this.screenRect.translate(gdloc.x, gdloc.y); screenCapCM = new DirectColorModel(24, /* red mask */ 0x00FF0000, /* green mask */ 0x0000FF00, /* blue mask */ 0x000000FF); bandmasks = new int[3]; bandmasks[0] = screenCapCM.getRedMask(); bandmasks[1] = screenCapCM.getGreenMask(); bandmasks[2] = screenCapCM.getBlueMask(); Toolkit.getDefaultToolkit().sync(); } public void autoResetGraphicsEnv() { this.screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); this.screen = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice(); } public void manuallySetGraphicsEnv(Rectangle screenRect, GraphicsDevice screen) { this.screenRect = screenRect; this.screen = screen; } public BufferedImage createBufferedScreenCapture(int pixels[]) throws HeadlessException, AWTException { // BufferedImage image; DataBufferInt buffer; WritableRaster raster; pixels = peer.getRGBPixels(screenRect); buffer = new DataBufferInt(pixels, pixels.length); raster = Raster.createPackedRaster(buffer, screenRect.width, screenRect.height, screenRect.width, bandmasks, null); return new BufferedImage(screenCapCM, raster, false, null); } public int[] createArrayScreenCapture() throws HeadlessException, AWTException { return peer.getRGBPixels(screenRect); } public WritableRaster createRasterScreenCapture(int pixels[]) throws HeadlessException, AWTException { // BufferedImage image; DataBufferInt buffer; WritableRaster raster; pixels = peer.getRGBPixels(screenRect); buffer = new DataBufferInt(pixels, pixels.length); raster = Raster.createPackedRaster(buffer, screenRect.width, screenRect.height, screenRect.width, bandmasks, null); // SunWritableRaster.makeTrackable(buffer); return raster; } }

En esencia, todo lo que he cambiado del original es mover muchas de las asignaciones de los cuerpos de las funciones, y establecerlas como atributos de la clase para que no se llamen todas las veces. Hacer esto realmente tuvo un efecto significativo en la velocidad de fotogramas. Incluso en mi computadora portátil severamente bajo, pasó de ~ 4 fps con la clase de stock Robot, a ~ 30 fps con mi clase FastRobot.

Primer examen:

Cuando comencé a realizar errores de memoria en mi programa principal, establecí esta prueba muy simple para vigilar el FastRobot. Nota: este es el código que produjo el perfil de montón arriba.

public class TestFBot { public static void main(String[] args) { try { FastRobot fbot = new FastRobot(); double startTime = System.currentTimeMillis(); for (int i=0; i < 1000; i++) fbot.createArrayScreenCapture(); System.out.println("Time taken: " + (System.currentTimeMillis() - startTime)/1000.); } catch (AWTException e) { e.printStackTrace(); } } }

Examinado:

No hace esto cada vez, lo cual es realmente extraño (¡y frustrante!). De hecho, rara vez lo hace en absoluto con el código anterior. Sin embargo, el problema de memoria se vuelve fácilmente reproducible si tengo múltiples bucles for back.

Prueba 2

public class TestFBot { public static void main(String[] args) { try { FastRobot fbot = new FastRobot(); double startTime = System.currentTimeMillis(); for (int i=0; i < 1000; i++) fbot.createArrayScreenCapture(); System.out.println("Time taken: " + (System.currentTimeMillis() - startTime)/1000.); startTime = System.currentTimeMillis(); for (int i=0; i < 500; i++) fbot.createArrayScreenCapture(); System.out.println("Time taken: " + (System.currentTimeMillis() - startTime)/1000.); startTime = System.currentTimeMillis(); for (int i=0; i < 200; i++) fbot.createArrayScreenCapture(); System.out.println("Time taken: " + (System.currentTimeMillis() - startTime)/1000.); startTime = System.currentTimeMillis(); for (int i=0; i < 1500; i++) fbot.createArrayScreenCapture(); System.out.println("Time taken: " + (System.currentTimeMillis() - startTime)/1000.); } catch (AWTException e) { e.printStackTrace(); } } }

Examinado

El montón fuera de control ahora es reproducible, diría aproximadamente el 80% del tiempo. He visto todo el perfilador, y lo que más nota (creo) es que el recolector de basura aparentemente se detiene justo cuando comienza el cuarto y último ciclo.

El resultado del código anterior dio los siguientes tiempos:

Time taken: 24.282 //Loop1 Time taken: 11.294 //Loop2 Time taken: 7.1 //Loop3 Time taken: 70.739 //Loop4

Ahora, si sumas los primeros tres bucles, suman 42.676, lo que sospechosamente corresponde a la hora exacta en que el recolector de basura se detiene y la memoria aumenta.

Ahora, este es mi primer rodeo con perfiles, sin mencionar la primera vez que pensé en la recolección de basura, siempre fue algo que funcionaba mágicamente de fondo, así que no estoy seguro de qué, en todo caso, me he enterado.

Información de perfil adicional

Augusto sugirió mirar el perfil de la memoria. Hay más de 1500 int[] que se enumeran como "inalcanzables, pero aún no recopilados". Estas son seguramente las matrices int[] que peer.getRGBPixels() el peer.getRGBPixels() , pero por alguna razón no se destruyen. Esta información adicional, lamentablemente, solo se suma a mi confusión, ya que no estoy seguro de por qué el GC no los recopilaría

Perfil con argumento de montón pequeño -Xmx256m:

En sugerencia irrecuperable y Hot Licks, establezco el tamaño máximo de almacenamiento dinámico en algo significativamente más pequeño. Si bien esto impide que el salto de 1 gb en el uso de la memoria, todavía no explica por qué el programa se dispara a su tamaño máximo de almacenamiento dinámico al entrar en la 4 ª iteración.

Como puede ver, el problema exacto todavía existe, se ha reducido. ;) El problema con esta solución es que el programa, por alguna razón, sigue consumiendo toda la memoria que puede, también hay un cambio marcado en el rendimiento de fps desde la primera las iteraciones, que consumen muy poca memoria, y la iteración final, que consume tanta memoria como puede.

La pregunta sigue siendo ¿por qué se está hinchando en absoluto?

Resultados después de presionar el botón "Forzar recolección de basura":

A sugerencia de jtahlborn, presiono el botón de Force Garbage Collection . Funcionó maravillosamente. Va de 1gb de uso de memoria, hasta la línea base de 60mb o más.

Entonces, esta parece ser la cura. La pregunta ahora es, ¿cómo obligo programáticamente al GC a hacer esto?

Resultados después de agregar Peer local al alcance de la función:

En la sugerencia de David Waters, modifiqué la función createArrayCapture() para que contenga un objeto Peer local.

Lamentablemente, no hubo cambios en el patrón de uso de la memoria.

Todavía se vuelve enorme en la tercera o cuarta iteración.

Análisis de grupo de memoria:

Capturas de pantalla de las diferentes agrupaciones de memoria

Todas las piscinas:

Eden Pool:

Viejo Gen:


Casi todo el uso de la memoria parece estar en este grupo.

Nota: PS Survivor Space tuvo (aparentemente) 0 uso

Me quedan varias preguntas:

(a) ¿significa el gráfico Garbage Profiler lo que creo que significa? ¿O estoy confundiendo la correlación con la causalidad? Como dije, estoy en un área desconocida con estos problemas.

(b) Si es el recolector de basura ... ¿qué hago al respecto ...? ¿Por qué se detiene por completo y luego se ejecuta a un ritmo reducido para el resto del programa?

(c) ¿Cómo soluciono esto?

¿Que está pasando aqui?


Dijiste que moviste la creación de objetos de los métodos a los campos de la clase. Fue una de las dependencias que movió "par"? p.ej

peer = ((ComponentFactory)toolkit).createRobot(elRoboto, screen);

Bien podría ser que el par se aferre a todas las capturas de pantalla tomadas durante la vida del objeto, esto se borrará cuando el par se salga del alcance, final del método en Robot, la vida de la clase para FastRobot.

Intente trasladar la creación y el alcance de los pares a su método y vea cuál es la diferencia.

public int[] createArrayScreenCapture() throws HeadlessException, AWTException { RobotPeer localPeer = ((ComponentFactory)toolkit).createRobot(elRoboto, screen); return localPeer.getRGBPixels(screenRect); }

Intento 2

Entonces, esta parece ser la cura. La pregunta ahora es, ¿cómo forzo programáticamente que el GC haga esto?

Puede llamar a System.gc () para solicitar una recolección de basura. Tenga en cuenta que esta es una solicitud, no una demanda. La JVM solo ejecutará la recolección de basura si cree que vale la pena.

Como puede ver, el problema exacto todavía existe, se ha reducido. ;) El problema con esta solución es que el programa, por alguna razón, sigue consumiendo toda la memoria que puede, también hay un cambio marcado en el rendimiento de fps desde la primera las iteraciones, que consumen muy poca memoria, y la iteración final, que consume tanta memoria como puede.

La pregunta sigue siendo ¿por qué se está hinchando en absoluto?

La JVM intentará y solo ejecutará una Colección mayor de basura cuando sea absolutamente necesario (se usará la mayoría del montón). (Lea sobre Young Generation vs Old Generation y dentro de Young Generation, espacio de Eden y espacio de sobrevivientes). Por lo tanto, espere que las aplicaciones Java de larga duración o de memoria se queden cerca del tamaño de almacenamiento dinámico máximo. Vale la pena señalar que para que la memoria se mueva a la Vieja Generación debe sobrevivir a 3 carreras menores de GC (Eden => Survivor 1 => Survivor 2 => Vieja Generación [Dependiendo de qué JVM está ejecutando y qué esquema de GC ha seleccionado en argumentos de línea de comando.])

En cuanto a por qué cambia este comportamiento, podría ser cualquier cantidad de cosas. Este último ciclo es el más largo, ¿Bloque System.getCurrentTimeMillis () durante el tiempo suficiente para que el GC tenga un ir en un hilo diferente? ¿Entonces el problema solo se muestra en los bucles más largos? El proceso de tomar una captura de pantalla me suena bastante bajo, supongo que se implementó con una llamada al kernel del sistema operativo, ¿bloquea esto el proceso en el espacio kernal impidiendo que se ejecuten otros subprocesos? (lo que detendría el funcionamiento del gc en el hilo de fondo).

Eche un vistazo a http://www.javacodegeeks.com/2012/01/practical-garbage-collection-part-1.html para una introducción al mundo de la recolección de basura. O memoria de Java explicada (SUN JVM) para un montón de enlaces.

Espero que haya ayudado.


Intente especificar un recolector de basura manualmente.

Concurrent Mark Sweep es un buen propósito general que proporciona un buen equilibrio entre baja pausa y rendimiento razonable.

Si está en Java 7 o posterior Java 6, el recopilador G1 probablemente sea mejor, ya que también es capaz de evitar la fragmentación de la memoria.

Puede consultar la página de ajuste de recolección de basura de la máquina virtual Hotspot Java SE para obtener más información y sugerencias :-D