views:

294

answers:

5

Different websites are giving different opinions.

My understanding is this:

To clean up or reclaim the memory that an object occupies, the Garbage collector comes into action. (automatically is invoked???)

The garbage collector then dereferences the object. Sometimes, there is no way for the garbage collector to access the object. Then finalize is invoked to do a final clean up processing after which the garbage collector can be invoked.

is this right?

+6  A: 

The garbage collector is working automatically in the background (although it can be explicitly invoked, but the need for this should be rare). It basically cleans up only objects which are not referenced by other objects (granted, the full picture is more complicated, but this is the basic idea). So it does not change any references in any live objects. If an object can not be accessed from any live object, this means that it can be safely garbage collected.

Finalization was meant to clean up resources acquired by the object (not memory, but other resources, e.g. file handles, ports, DB connections etc.). However, it did not really work out :-(

  • it is unpredictable when finalize() will be called
  • in fact, there is no guarantee that finalize() will be called ever!

So even if it were guaranteed to be called, it would not be a good place to release resources: by the time it is called to free up all the DB connections you have opened, the system may have run out of free connections completely, and your app does not work anymore.

Péter Török
that makes a lot of sense. Thanks!
Karthik Kottapalli
+3  A: 

Nope. The finalize() method is run only if the garbage collector attempts to reclaim your object.

Any memory used by your object will (usually, I can't think of an exception) automatically be connected to your object and cleaned up along with it. Finalization, therefore, isn't meant for freeing memory, but rather any other resources your object may be associated with. For example, this could be used to close open files or database connections, or perhaps run some low-level code interfacing with the operating system to release some system-level resources.

Carl Smotricz
You can't say "if and only if"; you can say "only if", but there's no guarantee that it will be called at all.
Michael Myers
@mmyers: +1. reclaimed -> finalize has been run, but not the reverse.
danben
There's no "but" there: If the GC attempts to reclaim the memory, then finalize() is called.
Carl Smotricz
@mmyers: A little pedantic, perhaps - "if and only if" is a colloquialism meant to stress that the subject is not a given. I suspect Carl meant "if and when", thereby implying that it might be never.
Software Monkey
Yep, you're correct that it's not bidirectionally conditional. Edited.
Carl Smotricz
@Carl Smotricz: according to the Sun documentation I linked to, this doesn't seem to be correct. They claim that the GC will add finalizable objects to a queue, and a separate finalizer thread will call finalize(). These objects are then free (but not guaranteed) to be reclaimed.
danben
@Software Monkey thank you, that's exactly how I meant it. But since "IFF" is strongly overloaded in logic, I guess it was prone to misunderstanding.
Carl Smotricz
@danben You're right, of course. I've made my statement a little more precise but refer the OP to your answer for an even more accurate description, which I've given a well deserved +1.
Carl Smotricz
+2  A: 

From this article:

Any instances of classes that implement the finalize() method are often called finalizable objects. They will not be immediately reclaimed by the Java garbage collector when they are no longer referenced. Instead, the Java garbage collector appends the objects to a special queue for the finalization process. Usually it's performed by a special thread called a "Reference Handler" on some Java Virtual Machines. During this finalization process, the "Finalizer" thread will execute each finalize() method of the objects. Only after successful completion of the finalize() method will an object be handed over for Java garbage collection to get its space reclaimed by "future" garbage collection.

You are free to do virtually anything in the finalize() method of your class. When you do that, please do not expect the memory space occupied by each and every object to be reclaimed by the Java garbage collector when the object is no longer referenced or no longer needed. Why? It is not guaranteed that the finalize() method will complete the execution in timely manner. Worst case, it may not be even invoked even when there are no more references to the object. That means it's not guaranteed that any objects that have a finalize() method are garbage collected.

Also, this article from Sun has some nice diagrams explaining the process.

danben
+2  A: 

Actually, here's the behavior of the finalize() method:

Once the Garbage collector runs (the VM decides it needs to free up memory, you cannot force it to run) and decided to collect the memory from this object (which means there are NO references pointing to it anymore, from reachable objects at least), just before it deletes the memory occupied by it, it runs the method finalize() on the object. You can be sure that if garbage collected, the object will run finalize() just before it disappears, but you cannot be sure that it will get GC'ed at all so you shouldn't rely on the method to do any sanitizing at all. You should run sanitizing statements inside finally {} blocks and not use finalize() as it is not guaranteed to run.

Furthermore, some people have done performance tests and showed that the finalize method somewhat slows down creation/destruction of the object. I cannot remember the source so treat this info as not very reliable. :)

pnt
Finalisers caused performance problems in NIO. They were removed in 1.4.1 (or possibly 1.4.2). If you are using NIO, you are expected to know how to use `finally` correctly.
Tom Hawtin - tackline
+1  A: 

Finalization is used to clean up resources, which cannot be freed by the garbage collector. For example, consider a program which allocates (via some native API) resources directly from the OS. This usually yields some kind of "handle" (a UNIX file descriptor or Windows HANDLE, or something similar):

class Wrapper {
    private long handle;

    private Handle(long h) {
        handle = h;
    }

    private static native long getHandleFromOS();

    static Wrapper allocate() {
        return new Handle(getHandleFromOS());
    }
}

So, what happens, if your code allocates an instance of class Wrapper? Well the class allocates some kind of OS specific resource and keeps a reference to it (the handle) in a member variable. But what happens, when the last Java reference to a wrapper instance is lost? Now, the garbage collector will (at some point) reclaim the space of the now defunct wrapper instance. But what happens to the OS resource allocated by the wrapper? It will be leaked in the above scenario, which is a bad thing, if it is a costly resource, such as a file descriptor.

In order to allow your code to clean up in such a scenario, there is the finalize method.

class Wrapper {
    private long handle;

    private Handle(long h) {
        handle = h;
    }

    protected void finalize() {
        returnHandleToOS(handle);
    }

    private static native long getHandleFromOS();
    private static native void returnHandleToOS(long handle);

    static Wrapper allocate() {
        return new Handle(getHandleFromOS());
    }
}

Now, when the GC reclaims the space of a wrapper instance, the finalizer makes sure, that the resource is properly returned to the OS.

This sounds all nice, but as others have already pointed out, the downside is, that finalization is inherently unreliable: you do not know when the finalizer will be run. Worse: there are no guarantees that it will be run at all. So ist best to provide an dispose mechanism and use finalization only as safety-net in case, the clients of your class forget to properly dispose their references:

class Wrapper {
    private long handle;

    private Handle(long h) {
        handle = h;
    }

    protected void finalize() {
        if( handle != 0 ) returnHandleToOS(handle);
    }

    public void dispose() {
        returnHandleToOS(handle);
        handle = 0;
    }

    private static native long getHandleFromOS();
    private static native void returnHandleToOS(long handle);

    static Wrapper allocate() {
        return new Handle(getHandleFromOS());
    }
}
Dirk