Search code examples
javafinalizer

Meaning of "The finalize method is never invoked more than once by a JVM for any given object." in case of Object Resurrection?


As per Java Doc:

The finalize method is never invoked more than once by a Java virtual machine for any given object.

  1. Can the above policy help to make an object immortal? If I resurrect an object one time from finalize() method then will that object become immortal, because finalize() method cannot be called on that object second time by JVM?

OR

  1. If the object become eligible for GC again (suppose the another reference pointing to resurrected object becomes eligible for GC) then next time JVM will delete the object without calling the finalize() method for that object?

I have tried to verify it by some sample code and assuming the second one is correct, but need confirmation for my understanding.


Solution

  • As per ernest_k It's number 2. The object will be garbage-collected without a call to its finalize() method.

    Adding my sample code to complete this answer, which I created to verify.

    import java.util.HashSet;
    import java.util.Set;
    
    class Immortal {
    
        // making it public and non-fianl to modify from outside
        public static Set<Immortal> immortals = new HashSet<>();
    
        @Override
        protected void finalize() throws Throwable {
            System.out.println("Running finalize for " + this);
            immortals.add(this); // Resurrect the object by creating a new reference
        }
    }
    
    public class ObjectResurrection {
    
        public static void callGC() {
            // call garbage collection
            System.gc();
    
            // wait for some time to give chance to run garbage collection thread
            try {
                Thread.sleep(10 * 1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    
        public static void main(String[] args) {
            Immortal immortal1 = new Immortal();
            Immortal immortal2 = new Immortal();
    
            // print objects when they are alive.
            System.out.println("immortal1 = " + immortal1);
            System.out.println("immortal2 = " + immortal2);
    
            // print immortal set
            System.out.println("immortal set = " + Immortal.immortals);
    
            // make all objects garbage collectable
            immortal1 = null;
            immortal2 = null;
    
            callGC();
    
            // now objects will be null
            System.out.println("immortal1 = " + immortal1);
            System.out.println("immortal2 = " + immortal2);
    
            // but stays in immortal set
            System.out.println("immortal set = " + Immortal.immortals);
    
            // remove all objects from immortal set, and make them again eligible for GC
            Immortal.immortals.clear();
    
            callGC();
    
            // again print the immortal set
            // this time set will be empty, and those 2 objects will be destroyed by Garbage
            // Collection,
            // but this time they will not call finalize, because it is already called
            // you can notice no output from finalize method in the output this time
            System.out.println("immortal set = " + Immortal.immortals);
        }
    
    }
    

    Output of Above Program (on my machine):

    immortal1 = effective.java.item7.finalizer6.Immortal@70dea4e
    immortal2 = effective.java.item7.finalizer6.Immortal@5c647e05
    immortal set = []
    Running finalize for effective.java.item7.finalizer6.Immortal@5c647e05
    Running finalize for effective.java.item7.finalizer6.Immortal@70dea4e
    immortal1 = null
    immortal2 = null
    immortal set = [effective.java.item7.finalizer6.Immortal@5c647e05, effective.java.item7.finalizer6.Immortal@70dea4e]
    immortal set = []