为啥在这个单元测试中没有调用 finalize 函数?

Posted

技术标签:

【中文标题】为啥在这个单元测试中没有调用 finalize 函数?【英文标题】:Why does the finalize function not get called in this unit test?为什么在这个单元测试中没有调用 finalize 函数? 【发布时间】:2012-03-02 20:45:51 【问题描述】:

我正在尝试编写一个 Java 单元测试来测试调用终结器对对象的影响。

为了确保调用终结器,我使用了我在 *** 其他地方看到的 WeakReference 方法。

我的问题是,在这个测试中,TestFinalizer 的 finalize 方法永远不会被调用,即使 WeakReference 仅在一次迭代后就变为 null:

public class FinalizerTest     
    private static class TestFinalizer 
        public static class Callback 
            public int NumFinalize = 0;

            public void finalized()
                NumFinalize++;
            
        
        private Callback callback;

        public TestFinalizer(Callback callback)
            this.callback = callback;
        

        @Override
        public void finalize() throws Throwable 
            callback.finalized();
            super.finalize();
        
    

    @Test
    public void testForceFinalizer()
        TestFinalizer.Callback callback = new TestFinalizer.Callback();
        TestFinalizer testFinalizer = new TestFinalizer(callback); 
        // Try to force finalizer to be called
        WeakReference<Object> ref = new WeakReference<Object>(testFinalizer);
        testFinalizer = null;
        int maxTries = 10000, i=0;
        while (ref.get() != null && i<maxTries) 
            ++i;
            System.gc();
        
        if ( ref.get() != null )
            fail("testFinalizer didn't get cleaned up within maxTries");

        // Last line passes, next fails!
        assertEquals("Should be exactly one call to finalizer", 1, callback.NumFinalize);
    

【问题讨论】:

【参考方案1】:

您致电System.gc 只是一个建议,而不是命令。

没有明确保证会调用任何终结器。

在您的情况下,可能会在 VM 退出时调用终结器。

【讨论】:

【参考方案2】:

在单元测试中添加 Thread.sleep(3000) 解决了我机器上的这个问题:

 @Test
    public void testForceFinalizer() throws InterruptedException
    
    FinalizerTest.TestFinalizer f = new FinalizerTest.TestFinalizer(null);
    FinalizerTest.TestFinalizer.Callback callback =  f.new Callback();
    TestFinalizer testFinalizer = new TestFinalizer(callback); // Try to
                                   // force
                                   // finalizer
                                   // to be
                                   // called
    WeakReference<Object> ref = new WeakReference<Object>(testFinalizer);
    testFinalizer = null;
    int maxTries = 10000, i = 0;
    while (ref.get() != null && i < maxTries)
    
        ++i;
        System.gc();
    
    if (ref.get() != null)
        fail("testFinalizer didn't get cleaned up within maxTries"); // Last
                                     // line
                                     // passes,
                                     // next
                                     // fails!
    System.out.println("Value: " + callback.NumFinalize);
    Thread.sleep(3000);
    assertEquals("Should be exactly one call to finalizer", 1,
        callback.NumFinalize);

    System.out.println("Value after: " + callback.NumFinalize);

    

这是在 assertEquals 调用之前执行的。正如其他人所说,调用 System.gc() 是一个建议,如果系统选择,系统可以忽略你。另外,我还确保没有什么是静态的,不确定这是否重要。

【讨论】:

是的,Thread.sleep(3000);在那里也为我修好了。我想这是因为即使弱 ref 已经被清空,终结器线程仍然需要做它的事情......【参考方案3】:

调用 gc 方法表明 Java 虚拟机花费精力回收未使用的对象,以使它们当前占用的内存可用于快速重用。

System.gc() 不会触发垃圾回收。

如果您在 while 循环中添加 Thread.sleep(xxx) 并等待一段时间,您可能会很幸运地看到终结器被调用。(没有帮助,刚刚测试)

【讨论】:

以上是关于为啥在这个单元测试中没有调用 finalize 函数?的主要内容,如果未能解决你的问题,请参考以下文章

python中unittest 单元测试调用没有调用到测试用例,新手求指点

为啥在 Quick 的单元测试中多次调用 before- 和 afterEach 块?

java方法的参数 为啥有时会加上final关键字

为啥这个 ObjC 块在释放时不释放其捕获的引用?包括失败的单元测试

为啥我的矩阵旋转没有通过我学校的单元测试?

为啥?单元测试覆盖率中显示的类即使没有添加到测试目标中