Android: the GC doesn't respect SoftReferences?

隐身守侯 提交于 2019-12-03 13:23:44

问题


It seams that Dalvik's garbage collector doesn't respect SoftReferences and removes them as soon as possible, just like WeakReferences. I'm not 100% sure yet, but despite the fact that there is still ~3MB of free memory my SoftReferences get cleared after I see "GC freed bla-bla-bla bytes" in LogCat.

Also, I saw a comment by Mark Murphy here:

Except that it doesn't work on Android, at least in the 1.5 timeframe. I have no idea if the GC SoftReference bugs have been fixed. SoftReferences get GC'd too soon with this bug.

Is it true? Are SoftReferences not respected?

How to workaround this?


回答1:


After not receiving an answer I decided to make my own study. I've made a simple test to exercise the GC against 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);
    }
}

The idea is that I make few runs of the same code increasing stress on SoftReferences each time (by running more GC passes).

Results are pretty interesting: All runs pass just fine except for one!

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.

These test results are stable. I've tried many times and every time it is the same. So I believe it is indeed a bug in garbage collector.

CONCLUSION

So, what we learn out of this... Using SoftReferences in your code is pointless for Android 1.5-1.6 devices. For these devices you will not get the behavior you expect. I didn't try for 2.1, however.




回答2:


I reported this issue to Google: https://code.google.com/p/android/issues/detail?id=20015




回答3:


@JBM I've tried your TestCase on Nexus S (android4.2.2), all tests are failed. GC is more aggressive against SoftReference on android4.2.2



来源:https://stackoverflow.com/questions/4014033/android-the-gc-doesnt-respect-softreferences

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!