Android: GC не уважает SoftReferences? - PullRequest
       36

Android: GC не уважает SoftReferences?

10 голосов
/ 25 октября 2010

Это говорит о том, что сборщик мусора Dalvik не уважает SoftReferences, и удаляет их как можно скорее, как и WeakReferences.Я еще не уверен на 100%, но, несмотря на то, что все еще остается ~ 3 МБ свободной памяти, мои SoftReferences очищаются после того, как я вижу «GC освобождает байт-бла-бла» в LogCat.

Также,Я видел комментарий Марка Мерфи здесь :

За исключением того, что он не работает на Android, по крайней мере, в 1,5 раза.Я понятия не имею, были ли исправлены ошибки GC SoftReference.SoftReferences получит GC'd слишком рано с этой ошибкой.

Это правда?Разве SoftReferences не соблюдаются?

Как обойти это?

Ответы [ 3 ]

7 голосов
/ 30 октября 2010

Не получив ответа, я решил провести собственное исследование. Я сделал простой тест для проверки GC на 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);
    }
}

Идея состоит в том, что я делаю несколько прогонов одного и того же кода, увеличивая нагрузку на SoftReferences каждый раз (выполняя больше проходов GC).

Результаты довольно интересные: все пробеги проходят просто отлично, кроме одного!

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.

Эти результаты испытаний стабильны. Я пробовал много раз, и каждый раз это одно и то же. Поэтому я считаю, что это действительно ошибка в сборщике мусора.

ЗАКЛЮЧЕНИЕ

Итак, что мы узнали из этого ... Использование SoftReferences в вашем коде бессмысленно для устройств Android 1.5-1.6 . Для этих устройств вы не получите ожидаемое поведение. Однако я не пробовал 2.1.

1 голос
/ 14 июля 2013

@ JBM Я попробовал ваш TestCase на Nexus S (android4.2.2), все тесты провалилисьGC более агрессивен в отношении SoftReference на android4.2.2

1 голос
/ 13 сентября 2011

Я сообщил об этой проблеме в Google: https://code.google.com/p/android/issues/detail?id=20015

...