¿Cómo liberar memoria en Java?

146

¿Hay alguna forma de liberar memoria en Java, similar a la free()función de C ? ¿O establecer el objeto en nulo y confiar en GC es la única opción?

Felix
fuente
151
Ok ... vamos a aclarar una cosa. El hecho de que piense que algo es una mala práctica y no es algo para alentarlo, no lo hace digno de un voto negativo. Esta es una pregunta clara y válida, preguntando si hay una manera de liberar memoria en Java sin depender de la recolección de basura. Si bien puede desanimarse y, en general, no es útil o es una buena idea, no puede saber que no hay escenarios en los que se pueda requerir sin saber lo que Felix sabe. Puede que Felix ni siquiera esté planeando usarlo. Es posible que solo quiera saber si es posible. De ninguna manera merece un voto negativo.
Daniel Bingham
77
Para aclarar, eso está dirigido a quien haya votado en contra, no necesariamente comentarios anteriores.
Daniel Bingham

Respuestas:

96

Java usa memoria administrada, por lo que la única forma en que puede asignar memoria es mediante el newoperador, y la única forma en que puede desasignar la memoria es confiando en el recolector de basura.

Este documento técnico de administración de memoria (PDF) puede ayudar a explicar lo que está sucediendo.

También puede llamar System.gc()para sugerir que el recolector de basura se ejecute inmediatamente. Sin embargo, Java Runtime toma la decisión final, no su código.

De acuerdo con la documentación de Java ,

Llamar al método gc sugiere que la máquina virtual Java hace un esfuerzo para reciclar objetos no utilizados con el fin de hacer que la memoria que ocupan actualmente esté disponible para su reutilización rápida. Cuando el control regresa de la llamada al método, Java Virtual Machine ha hecho un gran esfuerzo para reclamar espacio de todos los objetos descartados.

Daniel Pryden
fuente
55
Obliga al Garbage Collector a ejecutarse. No obliga a la memoria gratis, aunque ...
Pablo Santa Cruz
13
No Pablo, no obliga al GC a correr.
Jesper
1
Una persona muy confiable me ha dicho que todos los recolectores de basura de HotSpotVM ignoran por System.gc()completo.
Esko
1
En winXp java SE GC ejecuta todos los System.gc () o casi todos, pero API doc no lo garantiza.
teodozjan
2
@Pablo Santa Cruz ¿Qué quieres decir con que no libera memoria? ¿Acabo de probarlo en mi programa que parecía tener una fuga y el uso de RAM parecía estabilizarse? Y Daniel acaba de decir que solo lo sugiere, entonces, ¿cómo es que el porcentaje de ram utilizado siempre se estabilizó cada vez que llamé al método? Ustedes me están confundiendo.
65

Nadie parece haber mencionado explícitamente el establecimiento de referencias a objetos null, lo cual es una técnica legítima para "liberar" memoria que quizás desee considerar.

Por ejemplo, supongamos que declaró List<String>al principio de un método que creció en tamaño para ser muy grande, pero que solo fue necesario hasta la mitad del método. En este punto, podría establecer la referencia de Lista nullpara permitir que el recolector de basura pueda reclamar potencialmente este objeto antes de que se complete el método (y la referencia queda fuera de alcance de todos modos).

Tenga en cuenta que rara vez uso esta técnica en realidad, pero vale la pena considerarla al tratar con estructuras de datos muy grandes.

Adamski
fuente
8
Si realmente está haciendo mucho trabajo en un objeto que solo se usa para una parte de un método, sugiero tampoco; su método es demasiado complicado, divida el método en las porciones de antes y después, o use un bloque para la primera mitad del código (el último es más útil para los scripts de prueba)
Peter Lawrey
55
El lugar donde establecer una referencia de objeto como nulo es importante cuando se hace referencia desde otro objeto de larga duración (o posiblemente desde una var estática). Por ejemplo, si tiene una matriz de objetos grandes de larga duración y deja de usar uno de esos objetos, debe establecer la referencia de matriz en nula para que el objeto esté disponible para GC.
Hot Licks el
22
System.gc(); 

Ejecuta el recolector de basura.

Llamar al método gc sugiere que la máquina virtual Java hace un esfuerzo para reciclar objetos no utilizados con el fin de hacer que la memoria que ocupan actualmente esté disponible para su reutilización rápida. Cuando el control regresa de la llamada al método, la máquina virtual Java ha hecho un gran esfuerzo para reclamar espacio de todos los objetos descartados.

No recomendado.

Editar: escribí la respuesta original en 2009. Ahora es 2015.

Los recolectores de basura han mejorado constantemente en los ~ 20 años que Java ha existido. En este punto, si está llamando manualmente al recolector de basura, es posible que desee considerar otros enfoques:

  • Si está forzando GC en un número limitado de máquinas, puede valer la pena tener un punto de equilibrio de carga alejado de la máquina actual, esperando que termine de servir a los clientes conectados, agote el tiempo de espera después de un período de tiempo para colgar las conexiones y luego simplemente -reinicie la JVM. Esta es una solución terrible, pero si está mirando System.gc (), los reinicios forzados pueden ser una posible solución provisional.
  • Considere usar un recolector de basura diferente. Por ejemplo, el colector G1 (nuevo en los últimos seis años) es un modelo de pausa baja; usa más CPU en general, pero lo mejor es no forzar nunca una parada forzada en la ejecución. Dado que las CPU del servidor ahora casi todas tienen múltiples núcleos, esta es una muy buena solución de compromiso para tener disponible.
  • Mira el uso de la memoria de ajuste de tus banderas. Especialmente en las versiones más recientes de Java, si no tiene tantos objetos en ejecución a largo plazo, considere aumentar el tamaño de newgen en el montón. newgen (joven) es donde se asignan nuevos objetos. Para un servidor web, todo lo creado para una solicitud se coloca aquí, y si este espacio es demasiado pequeño, Java pasará más tiempo actualizando los objetos a la memoria de mayor duración, donde son más caros de matar. (Si newgen es un poco demasiado pequeño, lo pagará). Por ejemplo, en G1:
    • XX: G1NewSizePercent (el valor predeterminado es 5; probablemente no importa)
    • XX: G1MaxNewSizePercent (el valor predeterminado es 60; probablemente aumente esto)
  • Considera decirle al recolector de basura que no estás de acuerdo con una pausa más larga. Esto provocará ejecuciones de GC más frecuentes, para permitir que el sistema mantenga el resto de sus restricciones. En G1:
    • XX: MaxGCPauseMillis (el valor predeterminado es 200)
Dean J
fuente
1
Al comentar sobre mi propia publicación, esto a menudo no hace nada, y llamarlo repetidamente puede causar que la JVM se vuelva inestable y otras cosas. También puede atropellar a su perro; aproximación con precaución.
Dean J
1
Pondría un gran énfasis en la parte "sugiere" de "Llamar al método gc sugiere que el esfuerzo de expansión de JVM"
mate b
2
@Jesper, la respuesta de Dean dice "sugiere". De hecho, publicó la documentación exacta de los javadocs del método ...
b
2
@Software Monkey: Sí, podría haberlo editado. Pero como Dean J estaba obviamente activo (publicando hace solo unos minutos), pensé que era una cortesía pedirle que lo hiciera. Si no lo hubiera hecho, habría regresado aquí e hice la edición y borré mi comentario.
Daniel Pryden
1
También valdría la pena decir POR QUÉ no se recomienda. Si la JVM presta atención a la "sugerencia" de ejecutar el GC, casi seguramente hará que su aplicación funcione más lentamente, ¡posiblemente en muchos órdenes de magnitud!
Stephen C
11

* "Personalmente, confío en las variables de anulación como marcador de posición para una eliminación adecuada en el futuro. Por ejemplo, me tomo el tiempo para anular todos los elementos de una matriz antes de eliminar (hacer nula) la matriz en sí".

Esto es innecesario La forma en que funciona el GC de Java es que encuentra objetos que no tienen referencia a ellos, por lo que si tengo un Object x con una referencia (= variable) a que apunta a él, el GC no lo eliminará, porque hay una referencia a ese objeto:

a -> x

Si nulo a, esto sucede:

a -> null
     x

Entonces ahora x no tiene una referencia apuntando a él y será eliminado. Lo mismo sucede cuando configuras una referencia a un objeto diferente a x.

Entonces, si tiene una matriz arr que hace referencia a objetos x, y y z y una variable a que hace referencia a la matriz, se ve así:

a -> arr -> x
         -> y
         -> z

Si nulo a, esto sucede:

a -> null
     arr -> x
         -> y
         -> z

Por lo tanto, el GC considera que arr no tiene un conjunto de referencias y lo elimina, lo que le da esta estructura:

a -> null
     x
     y
     z

Ahora el GC encuentra x, y y z y también los elimina. Anular cada referencia en la matriz no mejorará nada, solo usará el tiempo y el espacio de la CPU en el código (dicho eso, no dolerá más que eso. El GC aún podrá funcionar de la manera que debería )

Dakkaron
fuente
5

Una razón válida para querer liberar memoria de cualquier programa (java o no) es hacer que haya más memoria disponible para otros programas en el nivel del sistema operativo. Si mi aplicación Java utiliza 250 MB, es posible que desee forzarla a 1 MB y hacer que los 249 MB estén disponibles para otras aplicaciones.

Yios
fuente
Si necesita liberar explícitamente una porción de 249 MB, en un programa Java, la administración de memoria no sería lo primero en lo que quisiera trabajar.
Marc DiMillo
3
Pero liberar almacenamiento dentro de su montón de Java no (en el caso general) hace que el almacenamiento esté disponible para otras aplicaciones.
Hot Licks el
5

Para ampliar la respuesta y el comentario de Yiannis Xanthopoulos y Hot Licks (lo siento, ¡no puedo comentar aún!), Puede configurar las opciones de VM como este ejemplo:

-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30

En mi jdk 7, esto liberará memoria de VM no utilizada si más del 30% del montón se libera después de GC cuando la VM está inactiva. Probablemente necesitará ajustar estos parámetros.

Si bien no lo vi enfatizado en el siguiente enlace, tenga en cuenta que algunos recolectores de basura pueden no obedecer estos parámetros y, de forma predeterminada, Java puede elegir uno de estos para usted, en caso de que tenga más de un núcleo (de ahí el argumento UseG1GC anterior) )

Argumentos de VM

Actualización: Para Java 1.8.0_73, he visto que la JVM ocasionalmente lanza pequeñas cantidades con la configuración predeterminada. Parece que solo lo hace si ~ 70% del montón no se usa, sin embargo ... no sé si sería más agresivo la liberación si el sistema operativo tuviera poca memoria física.

nsandersen
fuente
4

He hecho experimentos al respecto.

Es cierto que System.gc();solo sugiere ejecutar el recolector de basura.

Pero llamar System.gc();después de establecer todas las referencias a null, mejorará el rendimiento y la ocupación de la memoria.

Hemant Yadav
fuente
Creo que no puede decir con certeza que "llamar a System.gc (); después de establecer todas las referencias en nulo, mejorará el rendimiento y la ocupación de la memoria". Porque hay una gran complejidad computacional de System.gc (). E incluso después de llamar a System.gc () y, de hecho, recolectar la basura, es posible que jvm no devuelva la memoria al sistema operativo o al sistema. JVM puede retener la memoria para referencia futura. Mira esta respuesta .
Md. Abu Nafee Ibna Zahid
3

Si realmente desea asignar y liberar un bloque de memoria, puede hacerlo con ByteBuffers directos. Incluso hay una forma no portátil de liberar la memoria.

Sin embargo, como se ha sugerido, solo porque tenga que liberar memoria en C, no significa que sea una buena idea tener que hacer esto.

Si cree que realmente tiene un buen caso de uso gratis (), inclúyalo en la pregunta para que podamos ver lo que está tratando de hacer, es muy probable que haya una mejor manera.

Peter Lawrey
fuente
3

Completamente de javacoffeebreak.com/faq/faq0012.html

Un subproceso de baja prioridad se encarga de la recolección de basura automáticamente para el usuario. Durante el tiempo de inactividad, se puede invocar el subproceso y puede comenzar a liberar memoria previamente asignada a un objeto en Java. Pero no se preocupe, ¡no eliminará sus objetos!

Cuando no hay referencias a un objeto, se convierte en un juego justo para el recolector de basura. En lugar de llamar a alguna rutina (como gratuita en C ++), simplemente asigna todas las referencias al objeto como nulas, o asigna una nueva clase a la referencia.

Ejemplo:

public static void main(String args[])
{
  // Instantiate a large memory using class
  MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);

  // Do some work
  for ( .............. )
  {
      // Do some processing on myClass
  }

  // Clear reference to myClass
  myClass = null;

  // Continue processing, safe in the knowledge
  // that the garbage collector will reclaim myClass
}

Si su código está a punto de solicitar una gran cantidad de memoria, puede solicitar que el recolector de basura comience a reclamar espacio, en lugar de permitir que lo haga como un subproceso de baja prioridad. Para hacer esto, agregue lo siguiente a su código

System.gc();

El recolector de basura intentará recuperar espacio libre, y su aplicación puede continuar ejecutándose, con la mayor cantidad de memoria recuperada posible (pueden aplicarse problemas de fragmentación de memoria en ciertas plataformas).

nombre para mostrar
fuente
1

En mi caso, dado que mi código Java está destinado a ser portado a otros idiomas en un futuro próximo (principalmente C ++), al menos quiero pagar el servicio de labio para liberar la memoria correctamente, por lo que ayuda al proceso de portabilidad más adelante.

Personalmente, confío en las variables de anulación como marcador de posición para la eliminación adecuada en el futuro. Por ejemplo, me tomo el tiempo de anular todos los elementos de una matriz antes de eliminar (hacer nula) la matriz en sí.

Pero mi caso es muy particular, y sé que estoy tomando éxitos de rendimiento cuando hago esto.

Oskuro
fuente
1

* "Por ejemplo, supongamos que declaró una Lista al comienzo de un método que creció en tamaño para ser muy grande, pero que solo se requería hasta la mitad del método. En este punto, podría establecer la referencia de Lista como nula para permitir que el recolector de basura reclame potencialmente este objeto antes de que se complete el método (y la referencia queda fuera de alcance de todos modos) ". * *

Esto es correcto, pero esta solución puede no ser generalizable. Si bien establecer una referencia de objeto List a nulo-hará que la memoria esté disponible para la recolección de basura, esto solo es cierto para un objeto List de tipos primitivos. Si el objeto Lista contiene tipos de referencia, establecer el objeto Lista = nulo no desreferenciará -cualquier- de los tipos de referencia contenidos -en- la lista. En este caso, establecer el objeto Lista = nulo dejará huérfanos los tipos de referencia contenidos cuyos objetos no estarán disponibles para la recolección de basura a menos que el algoritmo de recolección de basura sea lo suficientemente inteligente como para determinar que los objetos han quedado huérfanos.

Gothri
fuente
1
Esto en realidad no es cierto. El recolector de basura Java es lo suficientemente inteligente como para manejarlo correctamente. Si anula la Lista (y los objetos dentro de la Lista no tienen otras referencias a ellos), el GC puede reclamar todos los objetos dentro de la Lista. Puede optar por no hacer eso en este momento, pero eventualmente los reclamará. Lo mismo ocurre con las referencias cíclicas. Básicamente, la forma en que funciona el GC es buscar de forma explícita objetos huérfanos y luego reclamarlos. Este es todo el trabajo de un GC. La forma en que lo describe haría que un GC sea completamente inútil.
Dakkaron
1

Althrough java proporciona recolección de basura automática, a veces querrá saber qué tan grande es el objeto y cuánto queda. Memoria libre usando programáticamente import java.lang;y Runtime r=Runtime.getRuntime(); para obtener valores de memoria usando mem1=r.freeMemory();para liberar memoria llame al r.gc();método yfreeMemory()

Benjamín
fuente
1

La recomendación de JAVA es asignar a nulo

De https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

Asignar explícitamente un valor nulo a las variables que ya no son necesarias ayuda al recolector de basura a identificar las partes de la memoria que pueden recuperarse de forma segura. Aunque Java proporciona administración de memoria, no evita pérdidas de memoria o el uso de cantidades excesivas de memoria.

Una aplicación puede provocar pérdidas de memoria al no liberar referencias de objetos. Hacerlo evita que el recolector de basura de Java reclame esos objetos y da como resultado el uso de cantidades cada vez mayores de memoria. Anular explícitamente las referencias a variables después de su uso permite al recolector de basura recuperar memoria.

Una forma de detectar pérdidas de memoria es emplear herramientas de creación de perfiles y tomar instantáneas de memoria después de cada transacción. Una aplicación libre de fugas en estado estable mostrará una memoria de montón activa estable después de la recolección de basura.

usuario3869837
fuente