¿Por qué es una mala práctica llamar a System.gc ()?

326

Después de responder una pregunta sobre cómo forzar objetos libres en Java (el chico estaba limpiando un HashMap de 1.5GB) System.gc(), me dijeron que era una mala práctica llamar System.gc()manualmente, pero los comentarios no fueron del todo convincentes. Además, nadie parecía atreverse a votar a favor ni a rechazar mi respuesta.

Allí me dijeron que es una mala práctica, pero también me dijeron que las ejecuciones del recolector de basura ya no detienen sistemáticamente al mundo, y que la JVM también podría usarlo efectivamente solo como una pista, así que estoy un poco en pérdida

Entiendo que la JVM generalmente sabe mejor que usted cuando necesita recuperar memoria. También entiendo que preocuparse por unos pocos kilobytes de datos es una tontería. También entiendo que incluso megabytes de datos no es lo que era hace unos años. Pero aún así, ¿1,5 gigabytes? Y sabes que hay como 1,5 GB de datos en memoria; No es como un tiro en la oscuridad. ¿Es System.gc()sistemáticamente malo o hay algún punto en el que se vuelve correcto?

Entonces la pregunta es en realidad doble:

  • ¿Por qué es o no es una mala práctica llamar System.gc()? ¿Es realmente solo una pista para la JVM bajo ciertas implementaciones, o es siempre un ciclo de recolección completo? ¿Realmente hay implementaciones de recolectores de basura que pueden hacer su trabajo sin detener el mundo? Por favor, arroje algo de luz sobre las diversas afirmaciones que la gente ha hecho en los comentarios a mi respuesta .
  • ¿Dónde está el umbral? ¿ Nunca es una buena idea llamar System.gc(), o hay momentos en que es aceptable? Si es así, ¿cuáles son esos tiempos?
zneak
fuente
44
Creo que un buen momento para llamar a System.gc () es cuando estás haciendo algo que ya es un proceso de carga largo. Por ejemplo, estoy trabajando en un juego y planeo llamar a System.gc () cuando el juego carga un nuevo nivel, al final del proceso de carga. El usuario ya está esperando un poco, y el aumento de rendimiento adicional puede valer la pena; pero también colocaré una opción en la pantalla de configuración para deshabilitar este comportamiento.
Ricket
41
Bozho: tenga en cuenta la primera palabra de la pregunta. ¿POR QUÉ es la mejor práctica no llamarlo? Solo repetir un mantra no explica una maldita cosa.
SOLO MI OPINIÓN correcta
1
Otro caso ha sido explicado en java-monitor.com/forum/showthread.php?t=188 donde explica cómo puede ser una mala práctica llamar al System.gc ()
Shirishkumar Bari el

Respuestas:

243

La razón por la que todos siempre dicen que se debe evitar System.gc()es que es un buen indicador de código fundamentalmente roto . Cualquier código que dependa de él para su corrección está ciertamente roto; cualquiera que dependa de él para el rendimiento probablemente esté roto.

No sabes bajo qué tipo de recolector de basura estás ejecutando. Ciertamente, hay algunos que no "detienen el mundo" como usted afirma, pero algunas JVM no son tan inteligentes o por varias razones (¿tal vez están en un teléfono?) No lo hacen. No sabes lo que va a hacer.

Además, no se garantiza que haga nada. La JVM puede ignorar por completo su solicitud.

La combinación de "no sabes lo que hará", "no sabes si incluso ayudará" y "no deberías llamarlo de todos modos" es la razón por la cual las personas son tan contundentes al decir que en general No deberías llamarlo. Creo que es un caso de "si necesita preguntar si debería usar esto, no debería"


EDITAR para abordar algunas preocupaciones del otro hilo:

Después de leer el hilo que vinculó, hay algunas cosas más que me gustaría señalar. Primero, alguien sugirió que las llamadas gc()pueden devolver la memoria al sistema. Eso ciertamente no es necesariamente cierto: el montón de Java en sí mismo crece independientemente de las asignaciones de Java.

Como en, la JVM mantendrá la memoria (muchas decenas de megabytes) y crecerá el montón según sea necesario. No necesariamente devuelve esa memoria al sistema incluso cuando libera objetos Java; es perfectamente libre de conservar la memoria asignada para usar en futuras asignaciones de Java.

Para mostrar que es posible que System.gc()no haga nada, vea:

http://bugs.sun.com/view_bug.do?bug_id=6668279

y en particular que hay una opción -XX: DisableExplicitGC VM.

Steven Schlansker
fuente
1
Es posible que pueda construir alguna configuración extraña de Rube Goldberg esque donde el método en el que se ejecuta el GC afecta la exactitud de su código. Quizás esté enmascarando alguna interacción extraña de subprocesos, o quizás un finalizador tenga un efecto significativo en la ejecución del programa. No estoy completamente seguro de que sea posible, pero puede ser, así que pensé en mencionarlo.
Steven Schlansker
2
@zneak, por ejemplo, podría haber puesto código crítico en los finalizadores (que es fundamentalmente código roto)
Martin
3
Me gustaría agregar que hay algunos casos en los que System.gc()es útil e incluso podría ser necesario. Por ejemplo, en aplicaciones de IU en Windows, puede acelerar enormemente el proceso de restauración de una ventana cuando llama a System.gc () antes de minimizar la ventana (especialmente cuando permanece minimizada durante bastante tiempo y partes del proceso se cambian a disco).
Joachim Sauer
2
@ AndrewJanke Yo diría que el código que usa WeakReferences para los objetos a los que desea retener es incorrecto desde el principio, recolección de basura o no. Tendría el mismo problema en C ++ con std::weak_ptr(aunque podría notar el problema en una versión de C ++ antes de lo que lo haría en una versión de Java ya que la destrucción de objetos no se aplazaría como suele ser la finalización).
JAB
2
@rebeccah Eso es un error, así que sí, lo llamaría 'ciertamente roto'. El hecho de que lo System.gc()arregle es una solución alternativa, no una buena práctica de codificación.
Steven Schlansker
149

Ya se ha explicado que las llamadas system.gc() pueden no hacer nada y que cualquier código que "necesita" que el recolector de basura se ejecute está roto.

Sin embargo, la razón pragmática por la que es una mala práctica llamar System.gc()es porque es ineficiente. Y en el peor de los casos, ¡es terriblemente ineficiente ! Dejame explicar.

Un algoritmo típico de GC identifica basura al atravesar todos los objetos que no son basura en el montón e inferir que cualquier objeto no visitado debe ser basura. A partir de esto, podemos modelar el trabajo total de una recolección de basura que consta de una parte que es proporcional a la cantidad de datos en vivo, y otra parte que es proporcional a la cantidad de basura; es decir work = (live * W1 + garbage * W2).

Ahora suponga que hace lo siguiente en una aplicación de subproceso único.

System.gc(); System.gc();

La primera llamada (predecimos) (live * W1 + garbage * W2)funcionará y eliminará la basura pendiente.

La segunda llamada (live* W1 + 0 * W2)funcionará y no reclamará nada. En otras palabras, hemos trabajado (live * W1)y no hemos logrado absolutamente nada .

Podemos modelar la eficiencia del recolector como la cantidad de trabajo necesaria para recolectar una unidad de basura; es decir efficiency = (live * W1 + garbage * W2) / garbage. Entonces, para que el GC sea lo más eficiente posible, necesitamos maximizar el valor de garbagecuando ejecutamos el GC; es decir, esperar hasta que el montón esté lleno. (Y también, haga que el montón sea lo más grande posible. Pero ese es un tema aparte).

Si la aplicación no interfiere (llamando System.gc()), el GC esperará hasta que el montón esté lleno antes de ejecutarse, lo que resulta en una recolección eficiente de basura 1 . Pero si la aplicación obliga a ejecutar el GC, lo más probable es que el montón no esté lleno, y el resultado será que la basura se recolecta de manera ineficiente. Y cuanto más a menudo la aplicación fuerza al GC, más ineficiente se vuelve el GC.

Nota: la explicación anterior pasa por alto el hecho de que un GC moderno típico divide el montón en "espacios", el GC puede expandir dinámicamente el montón, el conjunto de trabajo de la aplicación de objetos no basura puede variar, etc. Aun así, el mismo principio básico se aplica en todos los ámbitos a todos los recolectores de basura verdaderos 2 . Es ineficiente forzar el funcionamiento del GC.


1 - Así es como funciona el recopilador de "rendimiento". Los recolectores concurrentes como CMS y G1 utilizan diferentes criterios para decidir cuándo iniciar el recolector de basura.

2 - También estoy excluyendo los administradores de memoria que usan el recuento de referencias exclusivamente, pero ninguna implementación actual de Java usa ese enfoque ... por una buena razón.

Stephen C
fuente
45
+1 Buena explicación. Sin embargo, tenga en cuenta que este razonamiento solo se aplica si le importa el rendimiento. Si desea optimizar la latencia en puntos específicos, forzar GC puede tener sentido. Por ejemplo (hipotéticamente hablando) en un juego, es posible que desee evitar retrasos durante los niveles, pero no le importan los retrasos durante la carga de nivel. Entonces tendría sentido forzar el GC después de la carga nivelada. Disminuye el rendimiento general, pero eso no es lo que está optimizando.
sleske
2
@sleske: lo que dices es verdad. Sin embargo, ejecutar el GC entre niveles es una solución de ayuda de banda ... y no resuelve el problema de latencia si los niveles tardan lo suficiente como para que necesite ejecutar el GC durante un nivel de todos modos. Un mejor enfoque es usar un recolector de basura concurrente (pausa baja) ... si la plataforma lo admite.
Stephen C
No estoy completamente seguro de lo que quiere decir con "necesita que el recolector de basura se ejecute". Las condiciones que las aplicaciones deben evitar son; fallas de asignación que nunca pueden satisfacerse y altos costos generales de GC. Hacer llamadas al azar a System.gc () a menudo resulta en una alta sobrecarga de GC.
Kirk
@Kirk - "Hacer llamadas al azar a System.gc () a menudo resulta en una alta sobrecarga de GC". . Yo sé eso. Al igual que cualquiera que haya leído y entendido mi respuesta.
Stephen C
@Kirk - "No estoy completamente seguro de lo que quieres decir ..." - Me refiero a programas en los que el comportamiento "correcto" de un programa depende de la ejecución del GC en un momento determinado; por ejemplo, para ejecutar finalizadores, o para romper WeakReferences o SoftReferences. Es una muy mala idea hacer esto ... pero esto es de lo que estoy hablando. Ver también la respuesta de Steven Schlansker.
Stephen C
47

Parece que mucha gente te está diciendo que no hagas esto. Estoy en desacuerdo. Si, después de un gran proceso de carga, como cargar un nivel, cree que:

  1. Tienes muchos objetos inalcanzables y es posible que no hayan sido gc'ed. y
  2. Cree que el usuario podría soportar una pequeña desaceleración en este punto

no hay daño en llamar a System.gc (). Lo veo como la inlinepalabra clave c / c ++ . Es solo una pista para el gc de que usted, el desarrollador, ha decidido que el tiempo / rendimiento no es tan importante como suele ser y que parte de esto podría utilizarse para recuperar la memoria.

Consejos para no confiar en que hacer nada es correcto. No confíes en que funcione, pero dar la pista de que ahora es un momento aceptable para recolectar está perfectamente bien. Prefiero perder el tiempo en un punto del código donde no importa (pantalla de carga) que cuando el usuario interactúa activamente con el programa (como durante un nivel de un juego).

Hay un momento en que forzaré recopilación: al intentar averiguar si hay fugas de un objeto en particular (ya sea código nativo o interacción de devolución de llamada grande y compleja. Ah, y cualquier componente de la interfaz de usuario que eche un vistazo a Matlab). Esto nunca debe usarse en código de producción.

KitsuneYMG
fuente
3
+1 para GC al analizar las fugas de memoria. Tenga en cuenta que la información sobre el uso del almacenamiento dinámico (Runtime.freeMemory () et al.) En realidad solo es significativa después de forzar un GC, de lo contrario dependería de cuándo el sistema se molestó por última vez en ejecutar un GC.
sleske
44
no hay daño en llamar System.gc () podría requerir stop the worldenfoque y esto es un verdadero daño, si es que sucede
bestsss
77
No es malo en llamar el recolector de basura de forma explícita. Llamar al GC en el momento equivocado desperdicia los ciclos de la CPU. Usted (el programador) no tiene suficiente información para determinar cuándo es el momento adecuado ... pero la JVM sí.
Stephen C
Jetty hace 2 llamadas a System.gc () después del inicio y rara vez he visto que haga alguna diferencia.
Kirk
Bueno, los desarrolladores de Jetty tienen un error. Haría la diferencia ... incluso si la diferencia es difícil de cuantificar.
Stephen C
31

La gente ha estado haciendo un buen trabajo explicando por qué NO usarla, así que le contaré un par de situaciones en las que debe usarla:

(Los siguientes comentarios se aplican al Hotspot que se ejecuta en Linux con el recopilador CMS, donde me siento seguro al decir que System.gc()de hecho siempre invoca una recolección de basura completa).

  1. Después del trabajo inicial de iniciar su aplicación, puede tener un estado terrible de uso de memoria. La mitad de su generación permanente podría estar llena de basura, lo que significa que está mucho más cerca de su primer CMS. En aplicaciones donde eso importa, no es una mala idea llamar a System.gc () para "restablecer" su montón al estado inicial de los datos en vivo.

  2. En la misma línea que el n. ° 1, si monitorea de cerca su uso de almacenamiento dinámico, desea tener una lectura precisa de cuál es su uso de memoria de línea de base. Si los primeros 2 minutos del tiempo de actividad de su aplicación es todo inicialización, sus datos se desordenarán a menos que fuerce (ejem ... "sugerir") el gc completo por adelantado.

  3. Es posible que tenga una aplicación diseñada para nunca promocionar nada a la generación titular mientras se está ejecutando. Pero tal vez necesite inicializar algunos datos por adelantado que no sean tan grandes como para pasar automáticamente a la generación permanente. A menos que llame a System.gc () después de que todo esté configurado, sus datos podrían permanecer en la nueva generación hasta que llegue el momento de promocionarlos. De repente, su aplicación súper duper de baja latencia y baja GC se ve afectada por una penalización de latencia ENORME (relativamente hablando, por supuesto) por promover esos objetos durante las operaciones normales.

  4. A veces es útil tener una llamada System.gc disponible en una aplicación de producción para verificar la existencia de una pérdida de memoria. Si sabe que el conjunto de datos en vivo en el momento X debería existir en una proporción determinada con respecto al conjunto de datos en vivo en el momento Y, entonces podría ser útil llamar a System.gc () un tiempo X y un tiempo Y y comparar el uso de la memoria .

JT
fuente
1
Para la mayoría de los recolectores de basura generacionales, los objetos en la nueva generación tienen que sobrevivir a un cierto número (a menudo configurable) de recolecciones de basura, por lo que llamar System.gc()una vez para forzar la promoción de objetos no gana nada. Y seguramente no desea llamar System.gc()ocho veces seguidas y rezar para que ahora, la promoción se haya realizado y los costos ahorrados de una promoción posterior justifiquen los costos de múltiples GC completos. Dependiendo del algoritmo de GC, la promoción de muchos objetos puede que ni siquiera tenga costos reales, ya que solo reasignará la memoria a la generación anterior o la copiará simultáneamente ...
Holger
11

Esta es una pregunta muy molesta, y creo que contribuye a que muchos se opongan a Java a pesar de lo útil que es un lenguaje.

El hecho de que no pueda confiar en "System.gc" para hacer cualquier cosa es increíblemente desalentador y puede invocar fácilmente la sensación de "Miedo, incertidumbre, duda" al lenguaje.

En muchos casos, es bueno lidiar con los picos de memoria que causa a propósito antes de que ocurra un evento importante, lo que haría que los usuarios piensen que su programa está mal diseñado / no responde.

Tener la capacidad de controlar la recolección de basura sería una gran herramienta educativa, que a su vez mejoraría la comprensión de las personas sobre cómo funciona la recolección de basura y cómo hacer que los programas exploten su comportamiento predeterminado y su comportamiento controlado.

Permítanme revisar los argumentos de este hilo.

  1. Es ineficiente:

A menudo, el programa puede no estar haciendo nada y usted sabe que no está haciendo nada debido a la forma en que fue diseñado. Por ejemplo, podría estar haciendo una especie de larga espera con un gran cuadro de mensaje de espera, y al final también podría agregar una llamada para recolectar basura porque el tiempo de ejecución tomará una fracción muy pequeña del tiempo del espere mucho pero evitará que gc actúe en medio de una operación más importante.

  1. Siempre es una mala práctica e indica código roto.

No estoy de acuerdo, no importa qué recolector de basura tenga. Su trabajo es rastrear la basura y limpiarla.

Al llamar al gc durante los momentos en que el uso es menos crítico, reduce las probabilidades de que se ejecute cuando su vida se basa en el código específico que se ejecuta, pero en su lugar decide recolectar basura.

Claro, puede que no se comporte de la manera que desea o espera, pero cuando desea llamarlo, sabe que no está sucediendo nada y el usuario está dispuesto a tolerar la lentitud / tiempo de inactividad. Si el System.gc funciona, ¡genial! Si no es así, al menos lo intentaste. Simplemente no hay inconveniente a menos que el recolector de basura tenga efectos secundarios inherentes que hacen algo horriblemente inesperado en cuanto a cómo se supone que debe comportarse un recolector de basura si se invoca manualmente, y esto por sí mismo causa desconfianza.

  1. No es un caso de uso común:

Es un caso de uso que no puede lograrse de manera confiable, pero podría serlo si el sistema fue diseñado de esa manera. Es como hacer un semáforo y hacer que algunos / todos los botones de los semáforos no hagan nada, te hace preguntarte por qué el botón está ahí para empezar, javascript no tiene la función de recolección de basura, por lo que no No lo analice tanto por eso.

  1. La especificación dice que System.gc () es una pista de que GC debe ejecutarse y la VM es libre de ignorarlo.

¿Qué es una "pista"? ¿Qué es "ignorar"? una computadora no puede simplemente tomar pistas o ignorar algo, hay caminos de comportamiento estrictos que pueden ser dinámicos y guiados por la intención del sistema. Una respuesta adecuada incluiría lo que el recolector de basura realmente está haciendo, a nivel de implementación, que hace que no realice la recolección cuando lo solicita. ¿Es la función simplemente un nop? ¿Hay algún tipo de condiciones que deba cumplir? ¿Cuáles son estas condiciones?

Tal como está, el GC de Java a menudo parece un monstruo en el que simplemente no confías. No sabes cuándo va a ir o venir, no sabes qué va a hacer, cómo va a hacerlo. Me imagino que algunos expertos tienen una mejor idea de cómo funciona su Recolección de Basura por instrucción, pero la gran mayoría simplemente espera que "simplemente funcione", y tener que confiar en un algoritmo aparentemente opaco para que funcione para usted es frustrante.

Existe una gran brecha entre leer acerca de algo o aprender algo, y realmente ver su implementación, las diferencias entre sistemas y poder jugar con él sin tener que mirar el código fuente. Esto crea confianza y sentimiento de dominio / comprensión / control.

Para resumir, hay un problema inherente con las respuestas "esta característica podría no hacer nada, y no entraré en detalles sobre cómo saber cuándo hace algo y cuándo no hace y por qué no lo hará o lo hará, a menudo implica que es simplemente contra la filosofía intentar hacerlo, incluso si la intención detrás de esto es razonable ".

Puede estar bien que Java GC se comporte de la manera en que lo hace, o puede que no, pero para entenderlo, es difícil seguir realmente en qué dirección ir para obtener una visión general completa de lo que puede confiar en el GC y no hacer, por lo que es demasiado fácil simplemente desconfiar del lenguaje, porque el propósito de un lenguaje es tener un comportamiento controlado hasta un punto filosófico (es fácil para un programador, especialmente los novatos caer en una crisis existencial debido a ciertos comportamientos del sistema / lenguaje) son capaces de tolerar (y si no puedes, simplemente no usarás el lenguaje hasta que tengas que hacerlo), y más cosas que no puedes controlar sin razón conocida por la que no puedes controlarlas son inherentemente dañinas.

Dmitry
fuente
9

La eficiencia de GC se basa en una serie de heurísticas. Por ejemplo, una heurística común es que los accesos de escritura a objetos generalmente ocurren en objetos que se crearon no hace mucho tiempo. Otra es que muchos objetos tienen una vida muy corta (algunos objetos se usarán durante mucho tiempo, pero muchos se descartarán unos microsegundos después de su creación).

Llamar System.gc()es como patear el GC. Significa: "todos esos parámetros cuidadosamente ajustados, esas organizaciones inteligentes, todo el esfuerzo que solo pones en asignar y administrar los objetos de manera que las cosas salgan bien, bueno, simplemente deja todo y comienza desde cero". Se puede mejorar el rendimiento, pero la mayoría de las veces sólo se degrada el rendimiento.

Para usar de System.gc()manera confiable (*) necesita saber cómo funciona el GC en todos sus detalles. Tales detalles tienden a cambiar bastante si usa una JVM de otro proveedor, o la próxima versión del mismo proveedor, o la misma JVM pero con opciones de línea de comandos ligeramente diferentes. Por lo tanto, rara vez es una buena idea, a menos que desee abordar un problema específico en el que controle todos esos parámetros. De ahí la noción de "mala práctica": eso no está prohibido, el método existe, pero rara vez vale la pena.

(*) Estoy hablando de eficiencia aquí. System.gc()nunca romperá un programa Java correcto. Tampoco evocará memoria adicional que la JVM no podría haber obtenido de otra manera: antes de lanzar una OutOfMemoryError, la JVM hace el trabajo System.gc(), incluso como último recurso.

Thomas Pornin
fuente
1
+1 por mencionar que System.gc () no impide OutOfMemoryError. Algunas personas creen esto.
sleske
1
En realidad, puede evitar OutOfMemoryError debido al manejo de referencias suaves. Las SoftReferences creadas después de la última ejecución de GC no se recopilan en la implementación que conozco. Pero este es un detalle de implementación sujeto a cambios en cualquier momento y una especie de error y nada en lo que deba confiar.
maaartinus
8

A veces (¡ no a menudo! ) Realmente sabe más sobre el uso de la memoria pasada, actual y futura que el tiempo de ejecución. Esto no sucede muy a menudo, y afirmaría que nunca en una aplicación web mientras se sirven las páginas normales.

Hace muchos años trabajo en un generador de informes, que

  • Tenía un solo hilo
  • Lea la "solicitud de informe" de una cola
  • Cargó los datos necesarios para el informe de la base de datos.
  • Generó el informe y lo envié por correo electrónico.
  • Repetido para siempre, durmiendo cuando no había solicitudes pendientes.
  • No reutilizó ningún dato entre informes y no realizó ningún cobro.

En primer lugar, como no era en tiempo real y los usuarios esperaban un informe, un retraso mientras la ejecución del GC no era un problema, pero necesitábamos generar informes a un ritmo más rápido de lo solicitado.

Mirando el esquema anterior del proceso, queda claro que.

  • Sabemos que habrá muy pocos objetos vivos justo después de que se haya enviado un informe por correo electrónico, ya que la siguiente solicitud aún no ha comenzado a procesarse.
  • Es bien sabido que el costo de ejecutar un ciclo de recolección de basura depende de la cantidad de objetos vivos, la cantidad de basura tiene poco efecto sobre el costo de una ejecución de GC.
  • Que cuando la cola está vacía no hay nada mejor que hacer que ejecutar el GC.

Por lo tanto, claramente valía la pena hacer una ejecución de GC siempre que la cola de solicitudes estuviera vacía; No había inconveniente en esto.

Puede valer la pena realizar una ejecución de GC después de que se envíe cada informe por correo electrónico, ya que sabemos que este es un buen momento para una ejecución de GC. Sin embargo, si la computadora tuviera suficiente memoria RAM, se obtendrían mejores resultados al retrasar la ejecución del GC.

Este comportamiento se configuró en una base por instalación, para algunos clientes que habilitan un GC forzado después de cada informe acelerado la protección de los informes. (Supongo que esto se debió a la poca memoria en su servidor y a la ejecución de muchos otros procesos, por lo que un buen tiempo obligó a GC a reducir la paginación).

Nunca detectamos que una instalación que no se beneficiaba era una ejecución forzada del GC cada vez que la cola de trabajo estaba vacía.

Pero, seamos claros, lo anterior no es un caso común.

Ian Ringrose
fuente
3

Tal vez escribo un código malo, pero me he dado cuenta de que hacer clic en el icono de la papelera en los IDEs de eclipse y netbeans es una 'buena práctica'.

Ryan Fernandes
fuente
1
Esto puede ser así. Pero si Eclipse o NetBeans se programaron para llamar System.gc()periódicamente, probablemente encontraría el comportamiento molesto.
Stephen C
2

Primero, hay una diferencia entre especificaciones y realidad. La especificación dice que System.gc () es una pista de que GC debe ejecutarse y la VM es libre de ignorarlo. La realidad es que la VM nunca ignorará una llamada a System.gc ().

Llamar a GC viene con una sobrecarga no trivial a la llamada y si haces esto en algún momento aleatorio, es probable que no veas ninguna recompensa por tus esfuerzos. Por otro lado, es muy probable que una colección desencadenada naturalmente recupere los costos de la llamada. Si tiene información que indica que se debe ejecutar un GC, puede llamar a System.gc () y debería ver los beneficios. Sin embargo, según mi experiencia, esto sucede solo en algunos casos extremos, ya que es muy poco probable que tenga suficiente información para comprender si se debe llamar a System.gc ().

Un ejemplo enumerado aquí, golpear el bote de basura en su IDE. Si te vas a una reunión, ¿por qué no aciertas? Los gastos generales no le afectarán y el montón podría limpiarse para cuando regrese. ¡Haga esto en un sistema de producción y las frecuentes llamadas a cobrar lo detendrán! Incluso las llamadas ocasionales como las realizadas por RMI pueden ser perjudiciales para el rendimiento.

Iglesia
fuente
3
"La realidad es que la VM nunca ignorará una llamada a System.gc ()". - Incorrecto Lea sobre -XX: -DisableExplicitGC.
Stephen C
1

Sí, llamar a System.gc () no garantiza que se ejecutará, es una solicitud a la JVM que puede ignorarse. De los documentos:

Llamar al método gc sugiere que la máquina virtual Java hace un esfuerzo para reciclar objetos no utilizados

Casi siempre es una mala idea llamarlo porque la administración automática de memoria generalmente sabe mejor que usted cuándo usar gc. Lo hará cuando su grupo interno de memoria libre sea bajo, o si el sistema operativo solicita que se le devuelva algo de memoria.

Puede ser aceptable llamar a System.gc () si sabe que ayuda. Con eso quiero decir que ha probado y medido a fondo el comportamiento de ambos escenarios en la plataforma de implementación , y puede mostrar que ayuda. Sin embargo, tenga en cuenta que el gc no es fácilmente predecible: puede ayudar en una carrera y dañar en otra.

tom
fuente
<stroke> Pero también del Javadoc: _Cuando el control regresa de la llamada al método, la máquina virtual ha hecho su mejor esfuerzo para reciclar todos los objetos descartados, lo que veo como una forma más imperativa de lo que ha publicado. </ stroke > Al diablo con eso, hay un informe de error sobre que es engañoso. A partir de lo cual sabe mejor, ¿cuáles son los daños de insinuar la JVM?
zneak
1
El daño es que hacer la recolección en el momento equivocado puede ser una gran desaceleración. La pista que estás dando es probablemente mala. En cuanto al comentario del "mejor esfuerzo", pruébelo y vea en una herramienta como JConsole. A veces, hacer clic en el botón "Realizar GC" no hace nada
tom
Lamento no estar de acuerdo, pero llamar a System.gc () en OpenJDK y todo lo que se base en él (HP, por ejemplo) siempre da como resultado un ciclo de recolección de basura. De hecho, eso parece ser cierto para la implementación J9 de IBM también
Kirk
@Kirk - Incorrecto: google, y lee sobre -XX: -DisableExplicitGC.
Stephen C
0

En mi experiencia, usar System.gc () es efectivamente una forma de optimización específica de la plataforma (donde "plataforma" es la combinación de arquitectura de hardware, sistema operativo, versión JVM y posibles parámetros de tiempo de ejecución más, como RAM disponible), debido a su comportamiento, Si bien es más o menos predecible en una plataforma específica, puede (y variará) considerablemente entre plataformas.

Sí, no son situaciones en las que System.gc () mejorará el rendimiento (percibida). Un ejemplo es si los retrasos son tolerables en algunas partes de su aplicación, pero no en otras (el ejemplo del juego mencionado anteriormente, donde desea que ocurra GC al comienzo de un nivel, no durante el nivel).

Sin embargo, si ayudará o perjudicará (o no hará nada) depende en gran medida de la plataforma (como se definió anteriormente).

Por lo tanto, creo que es válido como una optimización de plataforma específica de último recurso (es decir, si otras optimizaciones de rendimiento no son suficientes). Pero nunca debe llamarlo solo porque cree que podría ayudar (sin puntos de referencia específicos), porque es probable que no lo haga.

sleske
fuente
0
  1. Dado que los objetos se asignan dinámicamente utilizando el nuevo operador,
    es posible que se pregunte cómo se destruyen dichos objetos y se
    libera su memoria para su posterior reasignación.

  2. En algunos lenguajes, como C ++, los objetos asignados dinámicamente deben liberarse manualmente mediante el uso de un operador de eliminación.

  3. Java toma un enfoque diferente; maneja la desasignación por usted automáticamente.
  4. La técnica que logra esto se llama recolección de basura. Funciona así: cuando no existen referencias a un objeto, se supone que ese objeto ya no es necesario, y la memoria ocupada por el objeto puede recuperarse. No hay una necesidad explícita de destruir objetos como en C ++.
  5. La recolección de basura solo ocurre esporádicamente (si es que lo hace) durante la ejecución de su programa.
  6. No ocurrirá simplemente porque existen uno o más objetos que ya no se usan.
  7. Además, las diferentes implementaciones de Java en tiempo de ejecución adoptarán diferentes enfoques para la recolección de basura, pero en su mayor parte, no debería tener que pensar al escribir sus programas.
Tanmay Delhikar
fuente