Android: ¿el GC no respeta SoftReferences?

Parece que el recolector de basura de Dalvik no respeta a 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 ~ 3 MB de memoria libre, mis SoftReferences se borran después de ver "GC liberado bla-bla-bla bytes" en LogCat.

También vi un comentario de Mark Murphy aquí :

Excepto que no funciona en Android, al menos en el marco de 1,5. No tengo ni idea si los errores de SoftReference de GC han sido arreglados. SoftReferences obtener GC'd demasiado pronto con este error.

¿Es verdad? ¿No se respetan las referencias SoftReferences?

¿Cómo solucionar esto?

3 Solutions collect form web for “Android: ¿el GC no respeta SoftReferences?”

Después de no recibir una respuesta decidí hacer mi propio estudio. He hecho una prueba sencilla 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 que hago algunas carreras del mismo código incrementando el estrés en SoftReferences cada vez (ejecutando más pases de GC).

Los resultados son bastante interesantes: ¡Todas las carreras pasan bien excepto una!

 En el dispositivo Android 1.5:
 TestSoftRefsAgainstGc_1 () FALLA!  AssertionFailedError: esperado: 0 pero fue: 499
 TestSoftRefsAgainstGc_2 () pasó
 TestSoftRefsAgainstGc_3 () pasó
 TestSoftRefsAgainstGc_4 () pasó
 TestSoftRefsAgainstGc_5 () pasó
 TestSoftRefsAgainstGc_6 () pasó
 TestSoftRefsAgainstGc_7 () pasó


 En el dispositivo Android 1.6:
 TestSoftRefsAgainstGc_1 () pasó
 TestSoftRefsAgainstGc_2 () FALLA!  AssertionFailedError: esperado: 0 pero fue: 499
 TestSoftRefsAgainstGc_3 () pasó
 TestSoftRefsAgainstGc_4 () pasó
 TestSoftRefsAgainstGc_5 () pasó
 TestSoftRefsAgainstGc_6 () pasó
 TestSoftRefsAgainstGc_7 () pasó

 En el dispositivo Android 2.2:
 Todo pasa.

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

CONCLUSIÓN

Así que, lo que aprendemos de esto … Usar SoftReferences en su código es inútil para dispositivos Android 1.5-1.6 . Para estos dispositivos no obtendrá el comportamiento que espera. No intenté para 2.1, sin embargo.

He informado de este problema a Google: https://code.google.com/p/android/issues/detail?id=20015

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

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.