¿Cuándo hace algo System.gc ()?

118

Sé que la recolección de basura está automatizada en Java. Pero entendí que si llama System.gc()a su código, la JVM puede o no decidir realizar la recolección de basura en ese momento. ¿Cómo funciona esto precisamente? ¿Sobre qué base / parámetros exactamente la JVM decide hacer (o no hacer) un GC cuando ve System.gc()?

¿Hay ejemplos en cuyo caso es una buena idea poner esto en su código?

Miguel
fuente
4
Un concepto demasiado complejo para todos los detalles aquí, pero este artículo debería ayudarlo: chaoticjava.com/posts/how-does-garbage-collection-work
GEOCHET
El comportamiento exacto depende de la JVM, es decir, depende de la implementación.
Thorbjørn Ravn Andersen

Respuestas:

59

En la práctica, suele decidir hacer una recolección de basura. La respuesta varía dependiendo de muchos factores, como en qué JVM se está ejecutando, en qué modo se encuentra y qué algoritmo de recolección de basura está utilizando.

No dependería de eso en tu código. Si la JVM está a punto de lanzar 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 en la práctica es en IDE, donde está adjunto a un botón en el que un usuario puede hacer clic, pero incluso allí no es muy útil.

Jodonnell
fuente
3
puede forzar una recolección de basura en jconsole
Benedikt Waldvogel
25
@bene ¿Realmente puedes "forzarlo"? ¿Jconsole solo está llamando a System.gc ()?
John Meagher
4
Lo usaría System.gc()en una pantalla de carga para un videojuego. En ese momento, realmente no me importaría si la llamada despejó todo lo posible, o no hizo nada en absoluto. Sin embargo, preferiría que "se esfuerce en reciclar los objetos no utilizados" durante la pantalla de carga, en lugar de durante el juego.
Kröw
30

El único ejemplo en el que se me ocurre dónde tiene sentido llamar a System.gc () es cuando se crea un perfil de una aplicación para buscar posibles fugas de memoria. Creo que los perfiladores llaman a este método justo antes de tomar una instantánea de la memoria.

Guillermo Vasconcelos
fuente
4
Sí, eso es lo que hago también. Los valores devueltos por Runtime.freeMemory (), por ejemplo, solo son realmente significativos después de System.gc (), porque normalmente desea saber cuánta memoria está bloqueada por instancias incobrables. Solo para ser utilizado en depuración / creación de perfiles de memoria, por supuesto, nunca en producción.
sleske
No, también es bueno para muchos otros escenarios. Por ejemplo, digamos que tiene un único patrón que tiene en cuenta el ciclo de vida. En onStop () si está anulando (ing) la instancia, es una buena idea llamar a System.gc () para ayudarlo.
portfoliobuilder
26

La especificación del lenguaje Java no garantiza que la JVM iniciará un GC cuando llame System.gc(). Esta es la razón de esto "puede que decida o no hacer un GC en ese momento".

Ahora, si observa el código fuente de OpenJDK , que es la columna vertebral de Oracle JVM, verá que una llamada a System.gc()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 recolector de basura que se ejecuta continuamente, por lo que una llamada a System.gc()no hará nada.

Alguna otra respuesta menciona el inicio de un GC en JConsole o VisualVM. Básicamente, estas herramientas realizan una llamada remota a System.gc().

Por lo general, no desea iniciar un ciclo de recolección de basura desde su código, ya que interfiere con la semántica de su aplicación. Su aplicación hace algunas cosas comerciales, la JVM se encarga de la administración de la memoria. Debe mantener esas preocupaciones separadas (no haga que su aplicación administre la memoria, céntrese en los negocios).

Sin embargo, hay pocos casos en los que una llamada a System.gc()pueda ser comprensible. Considere, por ejemplo, los microbenchmarks. Nadie quiere que suceda un ciclo de GC en 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.

Pierre Laporte
fuente
26

No tienes control sobre GC en Java: la VM decide. Nunca me he encontrado con un caso en el que System.gc()sea necesario . Dado que una System.gc()llamada simplemente SUGIERE que la VM haga una recolección de basura y también hace una recolección de basura COMPLETA (generaciones antiguas y nuevas en un montón multigeneracional), entonces en realidad puede causar que se consuman MÁS ciclos de CPU de lo necesario.

En algunos casos, puede tener sentido sugerirle a la máquina virtual que haga una recopilación completa AHORA, ya que puede saber que la aplicación estará inactiva durante los próximos minutos antes de que ocurra el levantamiento de objetos pesados. Por ejemplo, justo después de la inicialización de una gran cantidad de 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 eclipse que se inicia; 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.

DustinB
fuente
17

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

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

David Schlosnagle
fuente
Mi aplicación de Android siempre me arroja una OutOfMemoryException. Entonces 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
Sagar Devanga
2
@Sagar Devanga Llamar manualmente al gc como lo describe es poco probable que ayude. Antes de lanzar un OOM, la JVM ya habrá intentado recolectar basura para liberar memoria
Scrubbie
10

System.gc()es implementado por la máquina virtual, 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 una recopilación manual, el único momento en el que puede querer hacer esto es cuando abandona una colección grande que contiene un montón de colecciones más pequeñas, Map<String,<LinkedList>> por ejemplo, y desea probar y tomar el golpe de perforación entonces y allí, pero en su mayor parte, no debe preocuparse por eso. La CG sabe mejor que tú, lamentablemente, la mayor parte del tiempo.

Patricio
fuente
8

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

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

Peter Lawrey
fuente
¿Está diciendo que System.gc () recopila asignaciones directas mientras que la JVM normalmente no lo hace (antes de lanzar un OOM)? Porque creo que eso está mal. La única razón por la que una asignación puede tener éxito después de un GC explícito es el tiempo adicional y una mayor probabilidad de que un objeto en el montón con un finalizador se libere (y se libere algún objeto asignado directamente).
eckes
En la última versión de Java 6, el código en allocateBuffer siempre ejecutará System.gc () antes de lanzar un OutOfMemoryError. En el código de finalización tiene un atajo para el Cleanersque utiliza memoria directa. es decir, el subproceso finalizador no lo libera, ya que puede ser demasiado lento. Aun así, es posible obtener un OOME si está creando regiones de memoria directa particularmente rápido. La solución es no hacer eso y reutilizar sus regiones de memoria directa donde pueda.
Peter Lawrey
1
gracias, esto suena más a mi experiencia. Así que supongo que la respuesta solo es correcta para versiones anteriores de Java.
eckes
5

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

La especificación necesita aclaración: Error # 6668279: (spec) System.gc () debería indicar que no recomendamos su uso y no garantizamos el comportamiento

Internamente, RMI y NIO utilizan el método gc, y requieren ejecución síncrona, que: esto está actualmente en discusión:

Error # 5025281: Permitir que System.gc () active colecciones completas concurrentes (no para detener el mundo)

eckes
fuente
3

Garbage Collectiones bueno Javasi estamos ejecutando Software codificado en Java en Desktop / laptop / server. Puede llamar System.gc()o Runtime.getRuntime().gc()entrar Java.

Solo tenga en cuenta que ninguna de esas llamadas está garantizada para hacer algo. Son solo una sugerencia para que jvm ejecute el recolector de basura. Depende de 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 tuviera tiempo para eso.

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

Naveen
fuente
JVM ejecutará gc si tiene tiempo para eso : no siempre es cierto, Jvm puede ejecutar gc cuando la memoria de Eden está llena.
abdelmouheimen
2

Normalmente, la máquina virtual haría una recolección de basura automáticamente antes de lanzar una excepción OutOfMemoryException, por lo que agregar una llamada explícita no debería ayudar, excepto en que tal vez mueva el impacto de rendimiento a un momento anterior en el tiempo.

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

Cuando asigna en memoria un archivo, creo que la llamada map () arroja una IOException cuando no hay disponible un bloque de memoria lo suficientemente grande. Una recolección de basura justo antes del archivo map () podría ayudar a prevenir eso, creo. ¿Qué piensas?


fuente
2

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

lwpro2
fuente
2

Hay mucho que decir al tomarse el tiempo para probar las distintas 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 con memoria limitada y una cantidad relativamente grande de datos; hay algunos datos grandes que llevan mi entorno al límite, y aunque pude reducir el uso de la memoria, que en teoría debería funcionar bien, todavía obtendría errores de espacio de pila --- las opciones detalladas de GC me mostraron que estaba tratando de recolectar basura, pero fue en vano. En el depurador, podría ejecutar System.gc () y seguro que habría "mucha" memoria disponible ... no mucha 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 los grandes búferes necesarios para procesar los datos, y una prueba en la memoria libre disponible indica que no estoy garantizado para tenerlo. En particular, estoy viendo un entorno de 1 GB donde al menos 300 MB están ocupados 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 procesan tienen al menos 100-200 MB en la fuente. Todo es parte de un proceso de conversión automática de datos, por lo que todos los datos existen durante períodos de tiempo relativamente cortos a largo plazo.

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

Dicho todo esto, a pesar de que estoy usando System.gc (), seguí sintonizando el uso de parámetros de línea de comando 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 la mayoría de las veces.

Kyune
fuente
2

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

Sin embargo, no estoy 100% seguro de que la clase GarbageCollectorMXBean se haya introducido en la actualización 4 de Java 7, porque no pude encontrarla en las notas de la versión, pero encontré la información en el sitio javaperformancetuning .com

Shervin Asgari
fuente
0

No puedo pensar en un ejemplo específico cuando sea bueno ejecutar GC 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 pasa por cada objeto. Si este gc explícito termina siendo llamado repetidamente, podría conducir fácilmente a una aplicación lenta ya que se dedica mucho tiempo a ejecutar GC completos.

Alternativamente, si revisa el montón con un analizador de montón y sospecha que un componente de la biblioteca está llamando a GC explícitos, puede desactivarlo agregando: gc = -XX: + DisableExplicitGC a los parámetros de JVM.

zxcv
fuente
2
Llamamos a System.gc () para intentar cerrar nuestros objetos MappedByteBuffer que de otra manera no están cerrados y, por lo tanto, no están disponibles para otros procesos o para el truncamiento de archivos, aunque llamamos 'close ()' en los objetos. Desafortunadamente, todavía no siempre los cierra.
Tim Cooper
0

De acuerdo con Thinking in Java por Bruce Eckel, un caso de uso para la llamada explícita System.gc () es cuando desea forzar la finalización, es decir, la llamada para finalizar el método.

Asterisco
fuente
NB: hay un método propio para forzar la ejecución de la finalización. (En realidad, el problema no está en ejecutar finalizadores, sino en reconocer que un objeto se puede finalizar porque no tiene referencias)
eckes
-1

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

fleture
fuente