weakreference asynctask activity android garbage-collection weak-references soft-references

asynctask - Android: ¿el GC no respeta SoftReferences?



asynctask weakreference activity (3)

Parece que el recolector de basura de Dalvik no respeta SoftReferences y los elimina tan pronto como sea posible, al igual que WeakReferences. Todavía no estoy 100% seguro, pero a pesar de que todavía hay ~ 3MB de memoria libre, mis SoftReferences se borran después de ver "GC liberó bla-bla-bla bytes" en LogCat.

Además, vi un comentario de Mark Murphy aquí :

Excepto que no funciona en Android, al menos en el marco de tiempo de 1.5. No tengo idea si los errores de GC SoftReference han sido reparados. SoftReferences obtiene GC''d demasiado pronto con este error.

¿Es verdad? ¿No se respetan SoftReferences?

¿Cómo solucionar esto?


Después de no recibir una respuesta, decidí hacer mi propio estudio. Hice una prueba simple para ejercitar el GC contra SoftReferences.

public class TestSoftReference extends TestCase { public void testSoftRefsAgainstGc_1() { testGcWithSoftRefs(1); } public void testSoftRefsAgainstGc_2() { testGcWithSoftRefs(2); } public void testSoftRefsAgainstGc_3() { testGcWithSoftRefs(3); } public void testSoftRefsAgainstGc_4() { testGcWithSoftRefs(4); } public void testSoftRefsAgainstGc_5() { testGcWithSoftRefs(5); } public void testSoftRefsAgainstGc_6() { testGcWithSoftRefs(6); } public void testSoftRefsAgainstGc_7() { testGcWithSoftRefs(7); } private static final int SR_COUNT = 1000; private void testGcWithSoftRefs(final int gc_count) { /* "Integer(i)" is a referrent. It is important to have it referenced * only from the SoftReference and from nothing else. */ final ArrayList<SoftReference<Integer>> list = new ArrayList<SoftReference<Integer>>(SR_COUNT); for (int i = 0; i < SR_COUNT; ++i) { list.add(new SoftReference<Integer>(new Integer(i))); } /* Test */ for (int i = 0; i < gc_count; ++i) { System.gc(); try { Thread.sleep(200); } catch (final InterruptedException e) { } } /* Check */ int dead = 0; for (final SoftReference<Integer> ref : list) { if (ref.get() == null) { ++dead; } } assertEquals(0, dead); } }

La idea es hacer algunas ejecuciones del mismo código aumentando la tensión en SoftReferences cada vez (ejecutando más pases de GC).

Los resultados son bastante interesantes: ¡todas las carreras pasan sin problemas, excepto una!

On Android 1.5 device: testSoftRefsAgainstGc_1() FAILED! AssertionFailedError: expected:0 but was:499 testSoftRefsAgainstGc_2() passed testSoftRefsAgainstGc_3() passed testSoftRefsAgainstGc_4() passed testSoftRefsAgainstGc_5() passed testSoftRefsAgainstGc_6() passed testSoftRefsAgainstGc_7() passed On Android 1.6 device: testSoftRefsAgainstGc_1() passed testSoftRefsAgainstGc_2() FAILED! AssertionFailedError: expected:0 but was:499 testSoftRefsAgainstGc_3() passed testSoftRefsAgainstGc_4() passed testSoftRefsAgainstGc_5() passed testSoftRefsAgainstGc_6() passed testSoftRefsAgainstGc_7() passed On Android 2.2 device: All pass.

Estos resultados de prueba son estables. Lo he intentado muchas veces y cada vez es lo mismo. Así que creo que de hecho es un error en el recolector de basura.

CONCLUSIÓN

Entonces, lo que aprendemos de esto ... Usar SoftReferences en su código no tiene sentido para dispositivos Android 1.5-1.6 . Para estos dispositivos, no obtendrá el comportamiento que espera. No intenté para 2.1, sin embargo.



@JBM He probado su TestCase en Nexus S (android 4.2.2), todas las pruebas han fallado. GC es más agresivo contra SoftReference en android4.2.2