views:

434

answers:

8

I'm not quite understanding why there are finalizers in languages such as java and c#. AFAIK, they:

  • are not guaranteed to run (in java)
  • if they do run, they may run an arbitrary amount of time after the object in question becomes a candidate for finalization
  • and (at least in java), they incur an amazingly huge performance hit to even stick on a class.

So why were they added at all? I asked a friend, and he mumbled something about "you want to have every possible chance to clean up things like DB connections", but this strikes me as a bad practice. Why should you rely on something with the above described properties for anything, even as a last line of defense? Especially when, if something similar was designed into any API, said API would get laughed out of existence.

+14  A: 

Well, they are incredibly useful, in certain situations.

In the .NET CLR, for example:

  • are not guaranteed to run

The finalizer will always, eventually, run, if the program isn't killed. It's just not deterministic as to when it will run.

  • if they do run, they may run an arbitrary amount of time after the object in question becomes a candidate for finalization

This is true, however, they still run.

In .NET, this is very, very useful. It's quite common in .NET to wrap native, non-.NET resources into a .NET class. By implementing a finalizer, you can guarantee that the native resources are cleaned up correctly. Without this, the user would be forced to call a method to perform the cleanup, which dramatically reduces the effectiveness of the garbage collector.

It's not always easy to know exactly when to release your (native) resources- by implementing a finalizer, you can guarantee that they will get cleaned up correctly, even if your class is used in a less-than-perfect manner.

  • and (at least in java), they incur an amazingly huge performance hit to even stick on a class

Again, the .NET CLR's GC has an advantage here. If you implement the proper interface (IDisposable), AND if the developer implements it correctly, you can prevent the expensive portion of finalization from occuring. The way this is done is that the user-defined method to do the cleanup can call GC.SuppressFinalize, which bypasses the finalizer.

This gives you the best of both worlds - you can implement a finalizer, and IDisposable. If your user disposes of your object correctly, the finalizer has no impact. If they don't, the finalizer (eventually) runs and cleans up your unmanaged resources, but you run into a (small) performance loss as it runs.

Reed Copsey
Ok then i guess my question is more java specific :) i forgot finalizers are guaranteed to run in the .net framework
RCIX
Well, I suspect that, eventually, java will improve this situation. The current implementation doesn't guarantee they will run, but that may change in the future, and most of the same issues still exist in the java world.
Reed Copsey
Why would they have add finalizers if they are not guaranteed to run?
ChaosPandion
Realistically, they do eventually run. It's more a matter of thread priority in java, and the way the java GC is architected. Unfortunately, in practical purposes, they don't always get executed. Technically, they should eventually run, even in Java.
Reed Copsey
You can check the docs for finalize() - technically, they should always (eventually) run at some point after the object is a candidate for GC: http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Object.html#finalize()
Reed Copsey
A deadlocked finalizer will prevent all remaining finalizers from running as they are run sequentially.
Brian Rasmussen
A: 

In .Net land, t is not guaranteed when they run. But they will run.

BioBuckyBall
A: 

Are you refering to Object.Finalize?

According to msdn, "In C# code, Object.Finalize cannot be called or overridden". In fact, they recommend using the Dispose method because it is more controllable.

Jeremy
im talking about what is commonly known to C++ programmers as destrctors, or `~SomeClass()`.
RCIX
+1  A: 

They're meant for freeing up native resources (e.g. sockets, open files, devices) that can't be released until all references to the object have been broken, which is something that a particular caller would (in general) have no way of knowing. The alternative would be subtle, impossible-to-trace resource leaks...

Of course, in many cases as the application author you'll know that there's only one reference to the DB connection (for example); in which case finalizers are no substitute for closing it properly when you know you're finished with it.

SimonJ
+3  A: 

If you read the JavaDoc for finalize() it says it 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."

http://java.sun.com/javase/6/docs/api/java/lang/Object.html#finalize

So that's the "why". I guess you can argue whether their implementation is effective.

The best use I've found for finalize() is to detect bugs with freeing pooled resources. Most leaked objects will get garbage collected eventually and you can generate debug information.

class MyResource {
    private Throwable allocatorStack;

    public MyResource() {
        allocatorStack = new RuntimeException("trace to allocator");
    }

    @Override
    protected void finalize() throws Throwable {
        try {
            System.out.println("Bug!");
            allocatorStack.printStackTrace();
        } finally {
            super.finalize();
        }
    }
 }
hallidave
Printing debug information is probably the only useful thing you can do in a finalizer...
sleske
+3  A: 

In java finalizers exist to allow for the clean up of external resources (things that exist outside of the JVM and can't be garbage collected when the 'parent' java object is). This has always been rare. On example might be if you are interfacing with some custom hardware.

I think the reason that finalizers in java aren't guaranteed to run is that they might not have a chance to do so at program termination.

One thing you might do with a finalizer in 'pure' java is use it to test termination conditions- for example to check that all connections are closed and report an error if they are not. You aren't guaranteed that the error will be always caught but it will likely be caught at least some of the time which is enough to reveal a bug.

Most java code has no call for finalizers.

Alex Stoddard
+8  A: 

Hmya, you are getting a picture painted here that's a bit too rosy. Finalizers are not guaranteed to run in .NET either. Typical mishaps are a finalizer that throws an exception or a time-out on the finalizer thread (2 seconds).

That was a problem when Microsoft decided to provide .NET hosting support in SQL Server. The kind of application where restarting the app to solve resource leaks isn't considered a viable workaround. .NET 2.0 acquired critical finalizers, enabled by deriving from the CriticalFinalizerObject class. The finalizer of such a class must adhere to the rulez of constrained execution regions (CERs), essentially a region of code where exceptions are suppressed. The kind of things you can do in a CER are very limited.

Back to your original question, finalizers are necessary to release operating system resources other than memory. The garbage collector manages memory very well but doesn't do anything to release pens, brushes, files, sockets, windows, pipes, etc. When an object uses such a resource, it must make sure to release the resource after it is done with it. Finalizers ensure that happens, even when the program forgot to do so. You almost never write a class with a finalizer yourself, operating resources are wrapped by classes in the framework.

The .NET framework also has a programming pattern to ensure such a resource is released early so the resource doesn't linger around until the finalizer runs. All classes that have finalizers also implement the IDisposable.Dispose() method, allowing your code to release a resource explicitly. This is often forgotten by a .NET programmer but that doesn't typically cause problems because the finalizer ensures it will eventually be done. Many .NET programmers have lost hours of sleep worrying whether or not all Dispose() calls are taken care of and massive numbers of threads have been started about it on forums. Java folks must be a happier lot.


Following up on your comment: exceptions and timeouts in the finalizer thread is something that you don't have to worry about. Firstly, if you find yourself writing a finalizer, take a deep breath and ask yourself if you're on the Right Path. Finalizers are for framework classes, you should be using one of them to wrap the operating resource and rely on its finalizer. SafeHandle for example, it has a critical finalizer.

Secondly, finalizer thread failures are gross program failures. Similar to getting an OutOfMemory exception or tripping over the power cord and unplugging the machine. There isn't anything you can do about them, other than fixing the bug in your code or re-route the cable. It was important for Microsoft to design critical finalizers, they can't rely on all programmers that write .NET code for SQL Server to do the Right Thing. If you fumble a finalizer, it will probably be you that gets the service call from the customer, not Microsoft.

Hans Passant
But my question was since finalizers are not guaranteed to run (in java, and according to you C#) how can they be relied on to ensure anything?
RCIX
I expanded my answer, below the line.
Hans Passant
A: 

There's an additional complication with finalizers in .NET. If the class has a finalizer and does not get Dispose()'d, or Dispose() does not suppress the finalizer, the garbage collector will defer collecting until after compacting generation 2 memory (the last generation), so the object is "sort of" but not quite a memory leak. (Yes, it will get cleaned up eventually, but quite possibly not until application termination.)

As others have mentioned, if an object holds non-managed resources, it should implement the IDisposable pattern. Developers should be aware that if an object implements IDisposable, then it's Dispose() method should always be called. C# provides a way to automate this with the using statement:

using (myDataContext myDC = new myDataContext())
{
    // code using the data context here
}

The using block automatically calls Dispose() on block exit, even exits by return or exceptions being thrown. The using statement only works with objects that implement IDisposable.

And beware another confusion point; Dispose() is an opportunity for an object to release resources, but it does not actually release the Dispose()'d object. .NET objects are elligible for garbage collection when there are no active references to them. Technically, they can't be reached by any chain of object references, starting from the AppDomain.

Cylon Cat