Why are there finalizers in java and c#?

谁说胖子不能爱 提交于 2020-06-10 09:19:31

问题


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.


回答1:


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.




回答2:


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 such a class to use an operating resource, you'll get the finalizer built into that class for free. All the way down to the SafeHandle classes, they have 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 get that code right. If you fumble a finalizer yourself then there is no such liability, it will be you that gets the call from the customer, not Microsoft.




回答3:


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.




回答4:


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();
        }
    }
 }



回答5:


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.




回答6:


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




回答7:


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.




回答8:


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.




回答9:


The equivalent ofdestructor() in C++ is finalizer() in Java.

They are invoked when the life cycle of an object is about to end.



来源:https://stackoverflow.com/questions/1850742/why-are-there-finalizers-in-java-and-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!