Recolector de basura en Android

108

He visto muchas respuestas de Android que sugieren llamar al recolector de basura en algunas situaciones.

¿Es una buena práctica solicitar el recolector de basura en Android antes de realizar una operación que consume mucha memoria? Si no es así, ¿debería llamarlo solo si obtengo un OutOfMemoryerror?

¿Hay otras cosas que deba usar antes de recurrir al recolector de basura?

hpique
fuente

Respuestas:

144

Para versiones anteriores a 3.0 Honeycomb : Sí, llame System.gc() .

Traté de crear mapas de bits, pero siempre obtenía "VM sin memoria error". Pero, cuando llamé System.gc()primero, estaba bien.

Al crear mapas de bits, Android a menudo falla sin errores de memoria y no intenta recolectar basura primero . Por lo tanto, llame System.gc()y tendrá suficiente memoria para crear mapas de bits.

Si crea objetos, creo System.gcque se llamará automáticamente si es necesario, pero no para crear mapas de bits. Simplemente falla.

Así que recomiendo llamar manualmente System.gc()antes de crear mapas de bits.

steve
fuente
39
Esto se debe a que los datos de mapa de bits anteriores al panal no se almacenan en la VM y, por lo tanto, no activan el GC. No debería ser un problema en Honeycomb y posteriores.
Timmmm
2
@Timmmm, pero en realidad fue mi problema, simplemente configuré largeheap en verdadero.
Lei Leyba
118

En términos generales, en presencia de un recolector de basura, nunca es una buena práctica llamar manualmente al GC. Un GC se organiza en torno a algoritmos heurísticos que funcionan mejor cuando se dejan en sus propios dispositivos. Llamar al GC manualmente a menudo reduce el rendimiento.

Ocasionalmente , en algunas situaciones relativamente raras, uno puede encontrar que un GC en particular se equivoca y una llamada manual al GC puede mejorar las cosas, en términos de rendimiento. Esto se debe a que no es realmente posible implementar un GC "perfecto" que administre la memoria de manera óptima en todos los casos. Estas situaciones son difíciles de predecir y dependen de muchos detalles sutiles de implementación. La "buena práctica" es dejar que el GC funcione solo; una llamada manual al CG es la excepción, que solo debe contemplarse después de que se haya presenciado debidamente un problema de rendimiento real.

Thomas Pornin
fuente
8
+1 para una buena respuesta independiente de la plataforma. Esperando a ver si a alguien se le ocurre una respuesta específica de Android antes de elegir.
hpique
8
Estoy de acuerdo con lo que escribió si permite que "rendimiento" signifique algo más general que la eficiencia de la CPU. En un dispositivo Android, la percepción del rendimiento del usuario es primordial. Es posible que el desarrollador prefiera ejecutar el GC mientras el usuario presiona los botones, por ejemplo, para que el usuario no esté al tanto del GC.
Presidente James K. Polk
5
A menudo es importante en los juegos que el GC no se ejecute mientras el juego se está ejecutando (esto requiere que todos los objetos sean creados previamente y reciclados o similares), pero cuando el juego está en pausa, es un buen momento para GC.
Pat
4
Los datos de mapa de bits anteriores al panal no se almacenan en la memoria de la máquina virtual. El sistema no detectará cuando haya usado demasiada memoria que no sea de VM debido a mapas de bits y ejecute el GC (que ejecutaría los Bitmap.finalize()métodos que liberan la memoria que no es de VM). Por lo tanto, en este caso, debe pasar por encima de la cabeza del GC y ejecutar Bitmap.recycle()o System.gc()cuando corresponda. Pero solo pre-panal.
Timmmm
1
@ThomasPornin - Por otro lado, como programador de aplicaciones, sabes algo que el sistema operativo no sabe: momentos en los que tu aplicación ahora va a realizar un cambio importante en la forma en que usa la memoria, y que sería menos perjudicial para la experiencia del usuario para hacer una pausa ahora, que en un momento arbitrario en el futuro . Esto significa que puede ser sensato realizar llamadas poco frecuentes , en las transiciones importantes en cómo se usa la aplicación. Estos son raros en el sentido de que uno no debería llamar a GC con frecuencia, pero son comunes en el sentido de que casi cualquier aplicación significativa tiene momentos conocidos por diseño.
ToolmakerSteve
26

La falta de memoria en la aplicación de Android es muy común si no manejamos correctamente el mapa de bits.La solución al problema sería

if(imageBitmap != null) {
    imageBitmap.recycle();
    imageBitmap = null;
}
System.gc();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 3;
imageBitmap = BitmapFactory.decodeFile(URI, options);
Bitmap  scaledBitmap = Bitmap.createScaledBitmap(imageBitmap, 200, 200, true);
imageView.setImageBitmap(scaledBitmap);

En el código anterior, acabo de intentar reciclar el mapa de bits, lo que le permitirá liberar el espacio de memoria utilizado, por lo que es posible que no se agote la memoria. Lo intenté y funcionó para mí.

Si aún enfrenta el problema, también puede agregar estas líneas

BitmapFactory.Options options = new BitmapFactory.Options();
options.inTempStorage = new byte[16*1024];
options.inPurgeable = true;

para más información echa un vistazo a este enlace

https://web.archive.org/web/20140514092802/http://voices.yahoo.com/android-virtual-machine-vm-out-memory-error-7342266.html?cat=59


NOTA: Debido a la momentánea "pausa" causado por la realización de GC, se no se recomienda hacer esto antes de cada asignación de mapa de bits.

El diseño óptimo es:

  1. Libere todos los mapas de bits que ya no sean necesarios , mediante el if / recycle / nullcódigo que se muestra. (Crea un método para ayudarte con eso).

  2. System.gc();

  3. Asigne los nuevos mapas de bits.

yogesh
fuente
19

Si obtiene un OutOfMemoryError, generalmente es demasiado tarde para llamar al recolector de basura ...

Aquí hay una cita del desarrollador de Android:

La mayoría de las veces, la recolección de basura ocurre debido a toneladas de objetos pequeños y de corta duración y algunos recolectores de basura, como los recolectores de basura generacionales, pueden optimizar la recolección de estos objetos para que la aplicación no se interrumpa con demasiada frecuencia. Desafortunadamente, el recolector de basura de Android no puede realizar tales optimizaciones y, por lo tanto, la creación de objetos de corta duración en rutas de código críticas para el rendimiento es muy costosa para su aplicación.

Entonces, a mi entender, no hay una necesidad urgente de llamar al gc. Es mejor dedicar más esfuerzo a evitar la creación innecesaria de objetos (como la creación de objetos dentro de bucles)

Andreas Dolk
fuente
6
¿No se puede interpretar la cita al revés? Tal vez sea necesario llamar al GC manualmente para recopilar esos objetos antes de que consuman demasiada memoria.
hpique
@hgpc: No veo cómo se puede interpretar la cita de la manera que sugieres. Supongo que la documentación es una confesión, admitiendo que su GC es simple; cuando la memoria se agota, se ejecuta un GC completo.
Presidente James K. Polk
Hacer un uso completo de objetos complejos y marcos que usan objetos complejos tiende a aprovechar mejor el tiempo de desarrollo que la optimización prematura. Preocuparse por la optimización es una trampa más fácil en la que caer para Android que para Enterprise Java, ya que inconscientemente seguimos pensando en el rendimiento mientras codificamos una aplicación móvil. Especialmente porque los desarrolladores del framework, que tienen que pensar en el rendimiento, filtran ese punto de vista en su documentación oficial cuando no tienen cuidado.
LateralFractal
9

Parece System.gc()que no funciona en Art Android 6.0.1 Nexus 5x, así que lo uso Runtime.getRuntime().gc();en su lugar.

cmicat
fuente
1
System.gc()es una función contenedora para Runtime.getRuntime().gc(). Ver android.googlesource.com/platform/libcore/+/…
Nathan F.
Sí, desde la fuente y la documentación parece que son lo mismo, pero de hecho System.gc()no me funciona, ¡pero Runtime.getRuntime().gc()sí!
Ivan
7

Mi aplicación maneja muchas imágenes y murió con un OutOfMemoryError. Eso me ayudó. En el Manifest.xml Agregar

<application
....
   android:largeHeap="true"> 
Klykoo
fuente
9
A Google no le gusta esto
Pedro Paulo Amorim
1
@PedroPauloAmorim explica por qué?
Machado
2
No use largeHeap a menos que esté seguro de lo que está haciendo. El uso de montones grandes hace que el recolector de basura trabaje mucho más porque tiene que recorrer una gran cantidad de datos basura antes de borrar la memoria.
Prakash
7

En términos generales, no debe llamar a GC explícitamente con System.gc (). Incluso está la conferencia IO ( http://www.youtube.com/watch?v=_CruQY55HOk ) donde explican qué significa el registro de pausas de GC y en la que también afirman que nunca deben llamar a System.gc () porque Dalvik sabe mejor que tú cuando hacerlo.

Por otro lado, como se mencionó en las respuestas anteriores, el proceso GC en Android (como todo lo demás) a veces tiene errores. Esto significa que los algoritmos de Dalvik GC no están a la par con las JVM de Hotspot o JRockit y pueden equivocarse en algunas ocasiones. Una de esas ocasiones es cuando se asignan objetos de mapa de bits. Este es complicado porque usa memoria Heap y Non Heap y porque una instancia suelta de un objeto de mapa de bits en un dispositivo con restricción de memoria es suficiente para darle una excepción OutOfMemory. Entonces, llamarlo después de que ya no necesite este mapa de bits, generalmente es sugerido por muchos desarrolladores e incluso algunas personas lo consideran una buena práctica.

Una mejor práctica es usar .recycle () en un mapa de bits, ya que es para lo que está hecho este método, ya que marca la memoria nativa del mapa de bits como seguro para eliminar. Tenga en cuenta que esto depende mucho de la versión, lo que significa que generalmente será necesario en versiones anteriores de Android (creo que Pre 3.0) pero no será necesario en versiones posteriores. Además, no hará mucho daño usarlo en versiones más nuevas de ether (simplemente no hagas esto en un bucle o algo así). El nuevo tiempo de ejecución de ART cambió mucho aquí porque introdujeron una "partición" de Heap especial para objetos grandes, pero creo que no dolerá mucho hacer esto con ART ether.

También una nota muy importante sobre System.gc (). Este método no es un comando al que Dalvik (o JVM) estén obligados a responder. Considérelo más como decirle a la máquina virtual "¿Podría hacer la recolección de basura si no es una molestia?".

Igor Čordaš
fuente
3

Yo diría que no, porque los documentos del desarrollador sobre el estado de uso de RAM :

...
GC_EXPLICIT

Un GC explícito, como cuando llamas a gc () (que debes evitar llamar y, en cambio, confiar en el GC para que se ejecute cuando sea necesario).

...

He resaltado la parte relevante en negrita.

Eche un vistazo a la serie de YouTube, Patrones de rendimiento de Android : le mostrará consejos sobre cómo administrar el uso de la memoria de su aplicación (como el uso de ArrayMaps y SparseArrays de Android en lugar de HashMaps).

Farbod Salamat-Zadeh
fuente
3

Nota rápida para desarrolladores de Xamarin .

Si desea llamar System.gc()a las aplicaciones de Xamarin.Android, debe llamarJava.Lang.JavaSystem.Gc()

Denis Gordin
fuente
2

No es necesario llamar al recolector de basura después de un OutOfMemoryError.

Es Javadoc dice claramente:

Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector.

Por lo tanto, el recolector de basura ya intentó liberar memoria antes de generar el error, pero no tuvo éxito.

perdian
fuente
8
El Javadoc de Android no dice eso, y lo más probable es que su implementación sea diferente. d.android.com/reference/java/lang/OutOfMemoryError.html
hpique
Tiene razón en que esto no se aplica a Android. Pero, de nuevo, no puede manejar OOE en tiempo de ejecución ether, por lo que no puede hacer mucho al respecto, incluso si esto fuera o no fuera cierto.
Igor Čordaš