Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to ensure finalize() is always called (Thinking in Java exercise)

I'm slowly working through Bruce Eckel's Thinking in Java 4th edition, and the following problem has me stumped:

Create a class with a finalize( ) method that prints a message. In main( ), create an object of your class. Modify the previous exercise so that your finalize( ) will always be called.

This is what I have coded:

public class Horse {
    boolean inStable;
    Horse(boolean in){
        inStable = in;
    }   
    public void finalize(){
        if (!inStable) System.out.print("Error: A horse is out of its stable!");
    }
}
public class MainWindow {
    public static void main(String[] args) {
        Horse h = new Horse(false);
        h = new Horse(true);
        System.gc();
    }
}

It creates a new Horse object with the boolean inStable set to false. Now, in the finalize() method, it checks to see if inStable is false. If it is, it prints a message.

Unfortunately, no message is printed. Since the condition evaluates to true, my guess is that finalize() is not being called in the first place. I have run the program numerous times, and have seen the error message print only a couple of times. I was under the impression that when System.gc() is called, the garbage collector will collect any objects that aren't referenced.

Googling a correct answer gave me this link, which gives much more detailed, complicated code. It uses methods I haven't seen before, such as System.runFinalization(), Runtime.getRuntime(), and System.runFinalizersOnExit().

Is anybody able to give me a better understanding of how finalize() works and how to force it to run, or walk me through what is being done in the solution code?

like image 349
MattDs17 Avatar asked Aug 19 '12 22:08

MattDs17


People also ask

Is finalize method always called java?

Finalization: Just before destroying any object, the garbage collector always calls finalize() method to perform clean-up activities on that object. This process is known as Finalization in Java. Note: The Garbage collector calls the finalize() method only once on any object.

When finalize function method is called in java?

finalize() method in Java is a method of the Object class that is used to perform cleanup activity before destroying any object. It is called by Garbage collector before destroying the objects from memory. finalize() method is called by default for every object before its deletion.

When Finalize method is called and why?

The finalize method is called when an object is about to get garbage collected. That can be at any time after it has become eligible for garbage collection. Note that it's entirely possible that an object never gets garbage collected (and thus finalize is never called).

In which condition objects Finalize method is called?

finalize() is called by the garbage collector on an object when garbage collection determines that there are no more references to the object. A subclass overrides the finalize method to dispose of system resources or to perform other cleanup.


2 Answers

When the garbage collector finds an object that is eligible for collection but has a finalizer it does not deallocate it immediately. The garbage collector tries to complete as quickly as possible, so it just adds the object to a list of objects with pending finalizers. The finalizer is called later on a separate thread.

You can tell the system to try to run pending finalizers immediately by calling the method System.runFinalization after a garbage collection.

But if you want to force the finalizer to run, you have to call it yourself. The garbage collector does not guarantee that any objects will be collected or that the finalizers will be called. It only makes a "best effort". However it is rare that you would ever need to force a finalizer to run in real code.

like image 156
Mark Byers Avatar answered Oct 23 '22 14:10

Mark Byers


Outside of toy scenarios, it's generally not possible to ensure that a finalize will always be called on objects to which no "meaningful" references exist, because the garbage collector has no way of knowing which references are "meaningful". For example, an ArrayList-like object might have a "clear" method which sets its count to zero, and makes all elements within the backing array eligible to be overwritten by future Add calls, but doesn't actually clear the elements in that backing array. If the object has an array of size 50, and its Count is 23, then there may be no execution path by which code could ever examine the references stored in the last 27 slots of the array, but there would be no way for the garbage-collector to know that. Consequently, the garbage-collector would never call finalize on objects in those slots unless or until the container overwrote those array slots, the container abandoned the array (perhaps in favor of a smaller one), or all rooted references to the container itself were destroyed or otherwise ceased to exist.

There are various means to encourage the system to call finalize on any objects for which no strong rooted references happen to exist (which seems to be the point of the question, and which other answers have already covered), but I think it's important to note the distinction between the set of objects to which strong rooted references exist, and the set of objects that code may be interested in. The two sets largely overlap, but each set can contain objects not in the other. Objects' finalizers` run when the GC determines that the objects would no longer exist but for the existence of finalizers; that may or may not coincide with the time code they cease being of interest to anyone. While it would be helpful if one could cause finalizers to run on all objects that have ceased to be of interest, that is in general not possible.

like image 20
supercat Avatar answered Oct 23 '22 14:10

supercat