2008-09-15 12 views
93

? Sé que la recolección de basura está automatizada en Java. Pero entendí que si escribe System.gc() en su código, Java VM puede o no decidir en tiempo de ejecución hacer una recolección de basura en ese punto. ¿Cómo funciona esto exactamente? ¿En qué base/parámetros exactamente el VM decide hacer (o no hacer) un GC cuando ve un System.gc()? ¿Hay ejemplos, en cuyo caso es? Es una buena idea poner esto en su código.¿Cuándo System.gc() hace cualquier cosa

+0

El comportamiento exacto depende de la JVM, es decir, depende de la implementación. –

+4

Demasiado complejo de un concepto con detalles completos aquí, pero este artículo debería ayudarlo: http://chaoticjava.com/posts/how-does-garbage-collection-work/ – GEOCHET

Respuesta

46

En la práctica, por lo general decide hacer una recolección de basura. La respuesta varía según muchos factores, como en qué JVM se está ejecutando, en qué modo está y qué algoritmo de recolección de basura está usando.

No dependería de ello en su código. Si la JVM está a punto de arrojar un OutOfMemoryError, llamar a System.gc() no lo detendrá, porque el recolector de basura intentará liberar todo lo que pueda antes de llegar a ese extremo. La única vez que lo he visto usado en la práctica es en IDEs donde está conectado a un botón que un usuario puede hacer clic, pero incluso allí no es terriblemente útil.

+3

puede forzar una recolección de basura en jconsole –

+19

@bene ¿Puedes realmente "forzarlo"? ¿Jconsole simplemente llama a System.gc()? –

8

System.gc() es implementado por la VM, y lo que hace es específico de la implementación. El implementador podría simplemente regresar y no hacer nada, por ejemplo.

En cuanto a cuándo emitir un manual de recoger, el único momento en que es posible que desee hacer esto es cuando se abandona una gran colección que contiene un montón de pequeñas colecciones - un Map<String,<LinkedList>> por ejemplo - y quiere tratar de toma el golpe de perf entonces, pero en su mayor parte, no deberías preocuparte por eso. El GC lo sabe mejor que usted, por desgracia, la mayoría de las veces.

21

No tiene control sobre GC en Java: la VM decide. Nunca me he encontrado con un caso donde System.gc() es necesario. Como una llamada a System.gc() simplemente SUGIERE que la VM haga una recolección de elementos no utilizados y también una colección COMPLETA de elementos no utilizados (generaciones antiguas y nuevas en un montón multigeneracional), puede provocar MÁS ciclos de CPU que se deben consumir de lo necesario.

En algunos casos, puede tener sentido sugerir a la máquina virtual que realice una recolección completa AHORA, ya que es posible que sepa que la aplicación estará inactiva durante los próximos minutos antes de que se produzca un movimiento pesado. Por ejemplo, justo después de la inicialización de muchos objetos temporales durante el inicio de la aplicación (es decir, acabo de almacenar en caché una TONELADA de información, y sé que no obtendré mucha actividad durante un minuto más o menos). Piense en un IDE como el inicio de eclipse; hace mucho para inicializar, por lo que quizás inmediatamente después de la inicialización tenga sentido hacer un gc completo en ese punto.

1

En resumen:

Parámetros depende de la máquina virtual.

Ejemplo usage- no puede pensar en uno para aplicaciones en tiempo de ejecución/producción, pero es útil para ejecutarlo para algunos arneses de perfiles, como llamar

// test run #1 
test(); 
for (int i=0; i<10; i++) { 
// calling repeatedly to increase chances of a clean-up 
    System.gc(); 
} 
// test run #2 
test(); 
+14

¡Eso es gracioso! Yo hago lo mismo con los controles iguales. 'boolean isItReallyTrue = false; for (int i = 0; i <10; i ++) if (a == b) isItReallyTrue = true; '. Solo para estar seguro –

+0

el comentario más divertido que he visto – user2214913

1

No puedo pensar en un ejemplo específico cuando es bueno ejecutar CG explícito.

En general, ejecutar GC explícito en realidad puede causar más daño que bien, porque un gc explícito activará una colección completa, lo que lleva mucho más tiempo a medida que atraviesa cada objeto. Si este explícito gc termina siendo llamado repetidamente, podría llevar fácilmente a una aplicación lenta, ya que se gasta mucho tiempo ejecutando GC completos.

Alternativamente, si pasa por encima del montón con un analizador de pila y sospecha que un componente de biblioteca llamará a GC explícitos, puede desactivarlo agregando: gc = -XX: + DisableExplicitGC a los parámetros de JVM.

+2

Llamamos a System.gc() para tratar de cerrar nuestros objetos MappedByteBuffer que de otro modo no están cerrados, y por lo tanto no están disponibles para otros procesos o truncamiento de archivos, aunque llamemos 'cerrar'() 'en los objetos. Desafortunadamente, no siempre los cierra. –

17

Debe tener mucho cuidado si llama al System.gc(). Llamarlo puede agregar problemas de rendimiento innecesarios a su aplicación, y no se garantiza que realmente realice una colección. En realidad, es posible deshabilitar System.gc() explícitamente a través del argumento java -XX:+DisableExplicitGC.

Recomiendo leer los documentos disponibles en Java HotSpot Garbage Collection para obtener más detalles acerca de la recolección de basura.

+0

Mi aplicación Android siempre me arroja una OutOfMemoryException. Así que estaba pensando en usar System.gc() en la función onDestroy de mi clase para borrar todas las referencias y objetos no deseados. ¿Es un buen enfoque? –

+1

@Sagar Devanga Llamar manualmente al gc como usted describe es poco probable que ayude. Antes de lanzar un OOM, la JVM ya habrá intentado recolectar basura para liberar memoria. – Scrubbie

27

El único ejemplo en el que puedo pensar donde tiene sentido llamar a System.gc() es al crear un perfil de una aplicación para buscar posibles pérdidas de memoria. Creo que los perfiladores llaman a este método justo antes de tomar una instantánea de memoria.

+4

Sí, eso es lo que hago también. Los valores devueltos por Runtime.freeMemory() p. Ej. solo son realmente significativos después de un System.gc(), porque normalmente quiere saber cuánta memoria está bloqueada por instancias incobrables. Solo para ser utilizado en depuración/perfil de memoria, por supuesto, nunca en producción. – sleske

3

La mayoría de las JVM iniciarán un GC (según el conmutador -XX: DiableExplicitGC y -XX: + ExplicitGCInvokesConcurrent). Pero la especificación está menos definida para permitir mejores implementaciones más adelante.

La especificación necesita aclaración: Bug #6668279: (spec) System.gc() should indicate that we don't recommend use and don't guarantee behaviour

Internamente el método GC es utilizado por RMI y NIO, y requieren la ejecución sincrónica, que: este se encuentra actualmente en discusión:

Bug #5025281: Allow System.gc() to trigger concurrent (not stop-the-world) full collections

1

Normalmente, la VM haría una recolección de basura automáticamente antes de arrojar una OutOfMemoryException, por lo que agregar una llamada explícita no debería ser de ayuda, excepto en que tal vez mueva el golpe de rendimiento a un momento anterior en el tiempo.

Sin embargo, creo que encontré un caso en el que podría ser relevante. No estoy seguro de que, como todavía tengo que probar si tiene algún efecto:

Cuando mapa de memoria un archivo, creo que el mapa() llamada lanza una IOException cuando un gran bloque bastante de la memoria no es disponible. Creo que una recolección de basura justo antes del archivo map() podría ayudar a prevenir eso. ¿Qué piensas?

8

Si usa memorias intermedias de memoria directa, la JVM no ejecuta el GC por usted incluso si se está quedando sin memoria directa.

Si llama al ByteBuffer.allocateDirect() y obtiene un OutOfMemoryError, puede encontrar que esta llamada está bien después de activar un GC manualmente.

+0

¿Está diciendo que System.gc() recopila asignaciones directas mientras que JVM normalmente no (antes de lanzar un OOM). Porque creo que eso está mal. La única razón por la que una asignación podría suceder después de un GC explícito es el tiempo adicional y algo más de similitud de un objeto en el montón con un finalizador liberado (y liberando algún objeto directamente asignado). – eckes

+0

En la última versión de Java 6, el código en allocateBuffer siempre ejecutará un System.gc() antes de arrojar un OutOfMemoryError. En el código de finalización, tiene un atajo para "Limpiadores" de los que utiliza la memoria directa. es decir, no es liberado por el hilo finalizador ya que puede ser demasiado lento. Aun así, es posible obtener un OOME si está creando regiones de memoria directa particularmente rápidas. La solución es no hacer eso y volver a utilizar sus regiones de memoria directa donde pueda. –

+1

gracias, esto suena más como mi experiencia. Así que supongo que la respuesta solo es correcta para las versiones anteriores de Java. – eckes

1

nunca podemos forzar la recolección de basura. System.gc solo está sugiriendo vm para la recolección de basura, sin embargo, realmente a qué hora se ejecuta el mecanismo, nadie sabe, esto es como lo establecen las especificaciones de JSR.

2

Hay mucho que decir al tomarse el tiempo para probar las diversas configuraciones de recolección de basura, pero como se mencionó anteriormente, generalmente no es útil hacerlo.

Actualmente estoy trabajando en un proyecto que involucra un entorno de memoria limitada y una cantidad relativamente grande de datos: hay unos pocos datos grandes que llevan mi entorno al límite, y aunque pude llevar el uso de la memoria disminuyó, por lo que en teoría debería funcionar bien, todavía obtendría errores en el montón de espacio; las opciones de CG detalladas me mostraron que estaba intentando recolectar basura, pero fue en vano. En el depurador, podría ejecutar System.gc() y, efectivamente, habría "suficiente" memoria disponible ... no mucho más, pero suficiente.

En consecuencia, la única vez que mi aplicación llama a System.gc() es cuando está a punto de ingresar el segmento de código donde se asignarán grandes búferes necesarios para procesar los datos, y una prueba en la memoria libre disponible indica que No estoy garantizado de tenerlo. En particular, estoy viendo un entorno de 1 gb donde al menos 300mb está ocupado por datos estáticos, y la mayor parte de los datos no estáticos están relacionados con la ejecución, excepto cuando los datos que se están procesando tienen al menos 100-200 MB en la fuente. Todo es parte de un proceso automático de conversión de datos, por lo que todos los datos existen durante períodos relativamente cortos a largo plazo.

Desafortunadamente, si bien la información sobre las diversas opciones para ajustar el recolector de basura está disponible, parece un proceso experimental en gran medida y los detalles de nivel inferior necesarios para comprender cómo manejar estas situaciones específicas no se obtienen fácilmente.

Dicho todo esto, aunque estoy usando System.gc(), seguí sintonizando usando parámetros de línea de comandos y logré mejorar el tiempo de procesamiento general de mi aplicación en una cantidad relativamente significativa, a pesar de no poder superar el obstáculo que plantea trabajar con los bloques de datos más grandes. Dicho esto, System.gc() es una herramienta ... una herramienta muy poco confiable, y si no tiene cuidado con la forma en que la usa, deseará que no funcione con la mayor frecuencia.

0

Accroding al pensamiento en Java de Bruce Eckel, un caso de uso para explícita System.gc() llamada es cuando se quiere forzar la finalización, es decir, la llamada al finalizar método.

+0

NB: hay un método propio para forzar la ejecución de finalización. (El problema no es ejecutar finalizadores sino reconocer que un objeto se puede finalizar porque no se referencia) – eckes

0

mientras que system.gc funciona, detendrá el mundo: todas las respuestas se detienen para que el recolector de basura pueda escanear cada objeto para comprobar si es necesario eliminarlo. si la aplicación es un proyecto web, todas las solicitudes se detienen hasta que finalice, y esto hará que su proyecto web no pueda funcionar en un monent.

1

Si desea saber si se llama a su System.gc(), puede con la nueva actualización de Java 7 4 recibir una notificación cuando la JVM realice la recolección de elementos no utilizados.

No estoy 100% seguro de que la clase GarbageCollectorMXBean se introduce en Java 7 Update 4, sin embargo, porque no podía encontrar en las notas de la versión, pero encontré la información en el sitio de javaperformancetuning .com

0

Garbage Collection es bueno en Java, si estamos ejecutando software codificado en Java en Desktop/laptop/server. Puede llamar al System.gc() o Runtime.getRuntime().gc() en Java.

Solo tenga en cuenta que ninguna de esas llamadas garantiza hacer nada. Son solo una sugerencia para que jvm ejecute el recolector de basura. Es la JVM si ejecuta el GC o no. Entonces, respuesta corta: no sabemos cuándo se ejecuta. Respuesta más larga: JVM ejecutaría gc si tiene tiempo para eso.

Creo que lo mismo aplica para Android. Sin embargo, esto podría ralentizar su sistema.

17

La especificación de idioma de Java no garantiza que la JVM inicie un GC cuando llame al System.gc(). Esta es la razón de esto "puede o no decidir hacer un GC en ese momento".

Ahora, si nos fijamos en OpenJDK source code, que es la columna vertebral de Oracle JVM, se verá que una llamada a System.gc() se inicia un ciclo de GC. Si usa otra JVM, como J9, debe verificar su documentación para encontrar la respuesta.Por ejemplo, la JVM de Azul tiene un colector de basura que se ejecuta continuamente, por lo que una llamada al System.gc() no hará nada

Algunas otras respuestas mencionan el inicio de un GC en JConsole o VisualVM. Básicamente, estas herramientas hacen una llamada remota al System.gc().

Normalmente, no desea iniciar un ciclo de recolección de basura desde su código, ya que se confunde con la semántica de su aplicación. Su aplicación hace algunas cosas de negocios, la JVM se ocupa de la administración de la memoria. Debe mantener esas preocupaciones separadas (no haga que su aplicación haga una gestión de memoria, concéntrese en el negocio).

Sin embargo, hay pocos casos en que una llamada a System.gc() sea comprensible. Considere, por ejemplo, microbenchmarks. Nadie quiere que un ciclo de GC suceda en el medio de un microbenchmark. Por lo tanto, puede activar un ciclo de GC entre cada medición para asegurarse de que cada medición comience con un montón vacío.

Cuestiones relacionadas