安卓:GC不尊重软引用?

10

看起来,Dalvik的垃圾收集器不尊重SoftReference,并会尽快清除它们,就像WeakReference一样。虽然仍有约3MB的空闲内存,但我看到在LogCat中看到“GC freed bla-bla-bla字节”后,我的SoftReference被清除了。

此外,我在这里看到了Mark Murphy的评论:

除了在Android上不起作用(至少在1.5时期如此)。我不知道GC SoftReference错误是否已修复。由于该错误,SoftReferences也会过早地被GC回收。

这是真的吗?SoftReference没有得到尊重吗?

如何解决这个问题?


+1 对于这个问题和答案。干得好! - Gray
与https://dev59.com/qG025IYBdhLWcg3w25yy相关。 - Gray
3个回答

7

由于没有得到答案,我决定自己做一项研究。我进行了一项简单的测试,以检验GC对软引用的处理能力。

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 passes)。
结果非常有趣:除了一个之外,所有运行都通过了!
在Android 1.5设备上: testSoftRefsAgainstGc_1() 失败!AssertionFailedError: expected:0 but was:499 testSoftRefsAgainstGc_2() 通过 testSoftRefsAgainstGc_3() 通过 testSoftRefsAgainstGc_4() 通过 testSoftRefsAgainstGc_5() 通过 testSoftRefsAgainstGc_6() 通过 testSoftRefsAgainstGc_7() 通过
在Android 1.6设备上: testSoftRefsAgainstGc_1() 通过 testSoftRefsAgainstGc_2() 失败!AssertionFailedError: expected:0 but was:499 testSoftRefsAgainstGc_3() 通过 testSoftRefsAgainstGc_4() 通过 testSoftRefsAgainstGc_5() 通过 testSoftRefsAgainstGc_6() 通过 testSoftRefsAgainstGc_7() 通过
在Android 2.2设备上: 全部通过。
这些测试结果是稳定的。我尝试了很多次,每次都是一样的。所以我相信这确实是垃圾收集器中的一个bug。
结论是... 在Android 1.5-1.6设备中,使用SoftReferences在你的代码中是没有意义的。对于这些设备,你将无法得到你期望的行为。我没有尝试过2.1,不过。

1

@JBM,我已在Nexus S(android4.2.2)上尝试您的测试用例,所有测试均失败。在android4.2.2上,GC对SoftReference更加积极。


看起来这个策略是从Android2.3开始的。我使用System.gc()来进行显式垃圾回收。每次GC过程中,SoftReference对象几乎都被当作WeakReference回收了。 - jerry

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接