Why .NET Object has method Finalize()?
Asked Answered
R

2

7

I know that Finalize method is used by garbage collector to let object free up unmanaged resources. And from what I know, Object.Finalize is never called directly by GC (object is added to f-reachable queue during it's construction if it's type overrides the Finalize method by implementing finalizer).

Object.Finalize is only called from autogenerated finalizer code:

try
{
  //My class finalize implementation
}
finally
{
  base.Finalize(); // Here chain of base calls will eventually reach Object.Finalize/
}

So having an arbitrary class, derived from Object, wouldn't call Object.Finalize - you need finalizer for Object.Finalize to make sense and for most classes it doesn't make sense and is unused (not saying it's implementation is empty in fact).

Would it be too complex to check existence of Finalize method in a class without it overriding Object.Finalize, and generating root finalizer without try{}finally{base.Finalize()} call? Something similar to Add method for collection initializing - you don't have to implement any interface or override that method - just implement public void Add(item) method.

It would complicate C# compiler a bit, but make finalizer run slightly faster by removing one redundant call, and most importantly - make Object class easier to understand without having protected Finalize method with empty implementation while it doesn't need to finalize anything.

Also it might be possible to implement FinalizableObject class, derived from Object and make compiler derive all classes which have finalizer from that. It could implement IDisposable and make the disposing pattern, recommended by Microsoft reusable without need to implement it in every class. Actually I'm surprised such base class doesn't exist.

Ravid answered 11/10, 2017 at 14:50 Comment(2)
What makes you think you "need to implement [IDisposable] in every class"? That's definitely not true.Jaret
@JoelCoehoorn I didn't say we need IDisposable in every class. Of course we don't. So my question is why do we have Finalize in every class (inherited from Object)?Ravid
I
12

Edit

The garbage collection does not call the child implementation of Object.Finalise unless the method is overridden. Why is it available to all objects? So that it can be overridden when needed but unless it is there is no performance impact. Looking at documentation here, it states;

The Object class provides no implementation for the Finalize method, and the garbage collector does not mark types derived from Object for finalization unless they override the Finalize method.

Notes on finalization

Quoting directly from Ben Watson's excellent book Writing High-Performance .NET Code as he explains far better than I ever could;

Never implement a finalizer unless it is required. Finalizers are code, triggered by the garbage collector to cleanup unmanaged resources. They are called from a single thread, one after the other, and only after the garbage collector declares the object dead after a collection. This means that if your class implements a finalizer, you are guaranteeing that it will stay in memory even after the collection that should have killed it. This decreases overall GC efficiency and ensures that your program will dedicate CPU resources to cleaning up your object.

If you do implement a finalizer, you must also implement the IDisposable interface to enable explicit cleanup, and call GC.SuppressFinalize(this) in the Dispose method to remove the object from the finalization queue. As long as you call Dispose before the next collection, then it will clean up the object properly without the need for the finalizer to run. The following example correctly demonstrates this pattern;

class Foo : IDisposable
{
    ~Foo()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (disposing)
        {
            this.managedResource.Dispose();
        }
        // Cleanup unmanaged resource
        UnsafeClose(this.handle);
        // If the base class is IDisposable object, make sure you call:
        // base.Dispose();
    }
}

Note Some people think that finalizers are guaranteed to run. This is generally true, but not absolutely so. If a program is force-terminated then no more code runs and the process dies immediately. There is also a time limit to how long all of the finalizers are given on process shutdown. If your finalizer is at the end of the list, it may be skipped. Moreover, because finalizers execute sequentially, if another finalizer has an infinite loop bug in it, then no finalizers after it will ever run. While finalizers are not run on a GC thread, they are triggered by a GC so if you have no collections, the finalizers will not run. Therefore, you should not rely on finalizers to clean up state external to your process.

Microsoft has a good write up on finalizers and the Disposable pattern here

Ioneionesco answered 11/10, 2017 at 15:0 Comment(15)
Never implement a finalizer unless it is required. Exactly. And to further this, the only time it's required is when you are building a whole new kind of unmanaged resource. Example: you do not need a finalizer if you're building a data access layer that wraps the unmanaged database connection resources in SqlConnection (though it likely does need IDisposable), because the SqlConnection type already finalizes things for you. You'd only need a finalizer if you're building a the connection class for brand new never-before-seen database engine that needs to clean things up.Jaret
Personally I find this article by Stephen Cleary better than Microsoft's own documentation. It talks about splitting disposable classes in to two types. Ones that have unmanaged resources and derive from SafeHandle and ones that only own IDisposeable objects (SafeHandle is IDisposeable). With this pattern you should never need to write a finalizer.Transformer
@JoelCoehoorn "the only time it's required is when you are building a whole new kind of unmanaged resource" I disagree, I feel confidant enough to say that you should never write a finalizer. Anything that needs a finalizer should be encapsulated by a SafeHande instead and classes that are not derived from SafeHandle should only ever have managed resources in them.Transformer
@ScottChamberlain Taking the database connection example again, this means you couldn't really build that object in C#, and C# code could only get a safehandle to an object from a C++ (or similar) library. I'm not ready to make that concession.Jaret
@JoelCoehoorn You could, your MyNewEngineConnection class would be a normal class with all of the implmentation methods in it. Any calls to un-manged drivers can still be made, but any blobs returned from or given to those methods should not be a IntPtr but be a SafeHandle instead. Marshaling already provides automatic conversion from SafeHandle to IntPtr and back...Transformer
See the .NET 2.0 documentation of SafeHandleZeroOrMinusOneIsInvalid for a example of a blob allocated with AllocHGlobal which could be returned by a your unmanaged driver representing a "Data Row"Transformer
@ScottChamberlain But what if I don't want any unmanaged drivers? Say I'm implementing this in fully managed code, and the only reason there is an unmanaged resource at all is a limit on the server end for the number of active connections, where I'm concerned that an ungraceful disconnect will not let the server reclaim that connection promptly? IDisposable covers most of that scenario, but a finalizer handles some cases that IDisposable alone could miss.Jaret
That can be done too, see the section "Wrapping Unmanaged Resources - Defining Level 0 Types for Non-Pointer Data (The Hard Case)" in the article I linked to at the start of this for a semi-similar example, You could have a non pointer SafeHandle that represents the connection context and it can clean it self up in the ReleaseHandle method. However, I will concede that using a finalizer would result in shorter code in that specific situation.Transformer
Your discussion is directing when do we need to implement finalizers, but original question is a bit different area - why Microsoft added Finalize method to Object class instead of making slightly more coding in CLR engine and C# compiler and making Object class clear of finalization logic it doesn't need, doesn't use and doesn't implement? Am I missing something?Ravid
@OleksandrPshenychnyy, you're correct that the bulk directs to when - it was my initial take on what you were asking. I've since updated to answer the why behind the Object.Finalise() which basically boils down to "To allow any object to use it". As to why the made they decision at all, it was there call to make when designing .NET. I've left in my when response for anyone not familiar with finalizers or finalize functionality. Even an experienced developer might take your question as "Oh, I should be overriding that in my classes" which is most definitely not the case.Ioneionesco
@DiskJunky, yes, you tried to answer my question - "Why is it available to all objects? So that it can be overridden when needed". But as I described in question, it's not required in Object - GC could call Finalize, declared in some class, without having to override anything. And performance impact is real although small because of wrapping everything in extra try-cacheRavid
@OleksandrPshenychnyy, I agree but when designing a language, if you want to make a feature available to all objects in it, then putting a method, even a blank one on the root base class is a valid way to go. They could have put an interface on it but a root object should be as clean as possible. This is comparible to ToString(), GetHash() etc. Why put those methods on the root object? Same reasonIoneionesco
@Ioneionesco ToString and GetHash have implementations in Object and quite reasonable ones, so I don't think it's same situation. I also thought interface IFinalizable would be good idea, but when I tried to think deeper it appeared not that great - interface methods are public, and Finalize isn't intended to be public. Also they still would need to autogenerate wrapper try-cache somewhere and call base class finalizer if it is present. So to me having protected virtual Finalize method is better than interface, but it just shouldn't be only in classes implementing finalizer.Ravid
@OleksandrPshenychnyy, that's well reasoned but if you read through the MSDN documentation on the method it looks like it's quite closely tied to how the GC works. It would be easier to pick out if a class overrode the Finalize rather than try and pick out if it implemented an IFinalize. I suspect this may have been the reason why they went down the road of an empty Finalize method on the root object. There's heavy GC integration thereIoneionesco
@ScottChamberlain Since I'm the crazy performance guy and OP did talk about making finalization faster I feel obligated to point out you can use CriticalHandle instead of SafeHandle to the same effect. It is faster (with a safety trade-off naturally). I use CriticalHandle instead of SafeHandle as performance tends to be priority #1 in my world.Radionuclide
M
9

The C# language destructor syntax obscures too much about what a finalizer really does. Perhaps best demonstrated with a sample program:

using System;

class Program {
    static void Main(string[] args) {
        var obj = new Example();
        obj = null;    // Avoid debugger extending its lifetime
        GC.Collect();
        GC.WaitForPendingFinalizers();
        Console.ReadLine();
    }
}

class Base { ~Base() { Console.WriteLine("Base finalizer called"); } }
class Derived : Base { ~Derived() { Console.WriteLine("Derived finalizer called"); } }
class Example : Derived { }

Output:

Derived finalizer called
Base finalizer called

There are some noteworthy things about this behavior. The Example class itself does not have a finalizer, yet its base class finalizers are called anyway. That the Derived class finalizer is called before the Base class finalizer is not accidental. And note that the Derived class' finalizer has no call to base.Finalize(), even though the MSDN article for Object.Finalize() demands that it does, yet it is called anyway.

You may easily recognize this behavior, it is the way a virtual method behaves. One whose override calls the base method, like virtual method overrides commonly do. Which is otherwise exactly what it is inside the CLR, Finalize() is a plain virtual method like any other. The actual code generated by the C# compiler for the Derived class' destructor resembles this:

protected override Derived.Finalize() {
    try {
        Console.WriteLine("Derived finalizer called"); 
    }
    finally {
        base.Finalize();
    }
}

Not valid code, but the way it could be reverse-engineered from the MSIL. The C# syntax sugar ensures you can never forget to call the base finalizer and that it can't be aborted by a thread abort or AppDomain unload. The C# compiler does not otherwise help and auto-generate a finalizer for the Example class; the CLR does the necessary work of finding the finalizer of the most-derived class, traversing the method tables of the base classes until it finds one. And likewise helps in the class loader by setting a flag to indicate that Example has base classes with a finalizer so needs to be treated specially by the GC. The Base class finalizer calls Object.Finalize(), even though it doesn't do anything.

So key point is that Finalize() is actually a virtual method. It therefore needs a slot in the method table for Object so a derived class can override it. Whether it could have been done differently is pretty subjective. Certainly not easily and not without forcing every language implementation to special-case it.

Morbidezza answered 11/10, 2017 at 14:50 Comment(5)
example with 3 classes hierarchy is very explanative, and it proves that CLR still needs to walk through method tables to find out if there is finalizer defined somewhere. Same could be done searching finalize method by signature and not having it in Object class... Do you see any problems with such idea? I'm sure I must have overseen something...Ravid
Hmya, that is not what the CLR spec says it should do. It only provides the guarantee for one finalizer call and nothing else. Otherwise the reason why the C# compiler emits these base calls. Having them in a finalizer block is not demanded anywhere either, just the way the C# team preferred it. All very sensible choices.Morbidezza
I'm not against finally blocks. I'm just curious whether it all could have been done without having Finalize method in Object class. And I can't find any reason for it not being possible and by now nobody actually named any reason. Don't you agree FCL API would be cleaner if only objects that need finalization (those declaring finalizer and derived) had virtual method Finalize?Ravid
It will not be cleaner, far from it. The virtual method and the override don't have the same signature in the MSIL. Just as they don't in C# syntax. If Base does not have a finalizer and is refactored later to have one, any class with a finalizer that is derived from Base will break. Bummer if that class lives in another assembly. By giving Object a virtual Finalize method this can't go wrong since any finalizer will be override.Morbidezza
Good point. Might be enough reason of not doing that. I also just had an idea that if it was the way I described, not only CLR should have walked through method table, but C# compiler as well should know whether base class has finalizer and not sure it's easily achievable.Ravid

© 2022 - 2024 — McMap. All rights reserved.