Question

I'm trying to write a Java unit test that tests the effects of a call to a finalizer on an object.

In order to be sure the finalizer gets called I'm using a WeakReference method I saw elsewhere on stackoverflow.

My problem is that in this test the finalize method of TestFinalizer never gets called even though the WeakReference comes up null after just one iteration:

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);
    }
}
Était-ce utile?

La solution

Adding a Thread.sleep(3000) in the unit test fixed this issue on my machine:

 @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);

    }

This is performed right before the assertEquals call . As others have stated calling System.gc() is a suggestion and the system can ignore you if it so chooses. As an added, I also made sure nothing was static, not sure if that matters.

Autres conseils

Your call to System.gc is only a suggestion, not an order.

There is no explicit guarantee that any finalizers will be called.

In your case finalizer would probably be called when the VM exits.

Calling the gc method suggests that the Java Virtual Machine expend effort toward recycling unused objects in order to make the memory they currently occupy available for quick reuse.

System.gc() does not trigger a garbage collection.

You may be lucky to see the finalizer being called if you add a Thread.sleep(xxx) to your while loop and just wait some time. (doesn't help, just tested)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top