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

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

为了确保调用终结器,我使用了我在 stackoverflow 其他地方看到的 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);
    }
}

【问题讨论】:

    标签: java unit-testing finalizer finalize


    【解决方案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) 并等待一段时间,您可能会很幸运地看到终结器被调用。(没有帮助,刚刚测试)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-06-27
        • 1970-01-01
        • 2019-06-28
        • 2012-07-06
        • 2012-01-16
        • 1970-01-01
        • 2014-04-15
        相关资源
        最近更新 更多