开发者

Garbage collection Guarantees

What guarantees are the for the garbage collector?

From my research I have managed to find:

  • If there is still a reference to the memory it will not be garbage collected
  • If there is no reference:
    • When it is GC is non deterministic
    • When the GC kicks in the finalizer will be run before memory is released.
    • There is no guarantee about the order of Finalizers (so do not assume parent will be run before child).

But what I really want to know is:

Is there a guarantee that all memory will eventually be garbage collected and the finalizer (destructor) run on the object (assuming the program exited nicely). For example an application with no memory pressure when it eventually exits will it force the GC to go find all objects a开发者_运维技巧nd make sure the finalizer (destructor) is called (including static member variables)?

I did find a quote on this page: http://www.c-sharpcorner.com/UploadFile/tkagarwal/MemoryManagementInNet11232005064832AM/MemoryManagementInNet.aspx

In addition, by default, Finalize methods are not called for unreachable objects when an application exits so that the application may terminate quickly.

But I am not sure how authoritative this quote is.

I also found documentation on: CriticalFinalizerObject


Is there a guarantee that all memory will eventually be garbage collected and the finalizer (destructor) run on the object (assuming the program exited nicely).

No. From the Object.Finalize documentation it is clear that finalizers may not be invoked if

  • Some other finalizers don't finish properly:

    Another finalizer blocks indefinitely (goes into an infinite loop, tries to obtain a lock it can never obtain and so on). Because the runtime attempts to run finalizers to completion, other finalizers might not be called if a finalizer blocks indefinitely.

  • Some other finalizers create more finalizable objects, making it impossible to finalize all finalizable objects:

    The runtime continues to Finalize objects during shutdown only while the number of finalizable objects continues to decrease.

  • Some other finalizers throw exceptions:

    If Finalize or an override of Finalize throws an exception, and the runtime is not hosted by an application that overrides the default policy, the runtime terminates the process and no active try-finally blocks or finalizers are executed. This behavior ensures process integrity if the finalizer cannot free or destroy resources.

That being said, there are more reasons why you wouldn't want to use finalizers unless strictly necessary.

  • They slow down the garbage collector (even making it possible to slow it down so much that memory is not reclaimed as fast as it is used up).
  • They run on another thread, bringing multi-threading issues into play.
  • They're not executed in a deterministic order.
  • They can resurrect objects which were already finalized (and which won't be finalized again unless explicitly re-registered for finalization).


The only time you should write a finalizer is when you are building a type to handle a new kind of unmanaged resource. For example, a data access layer that uses Sql Server in a business app doesn't need a finalizer anywhere, even though there are unmanaged database connections involved, because the basic SqlConnection class will already finalize those connections if needed. But if you're building a brand new database engine from scratch that has connection limits similar to sql server's and are implementing the ado.net provider for it, that connection type should implement a finalizer to be as sure as possible that your connections are released.

But you don't get any guarantees beyond what happens when a process ends.

Update:

Given this context:

I am having a discussion with a collegue over a code review I did of his code. He insists that the destructor is guranteed to be called on an object. I disagree (but am not sure) and would prefer the use of IDisposable.

You are right to criticize the use of a destructor/finalizer. As I said above, you should only use them when working with an unmanaged resource that is genuinely new. Not just that instance of the resource, but the kind of resource you are working with.

For code that wraps "normal" unmanaged resources (things like SqlConnection, TcpClient, etc), IDisposable is a better choice. Then you know the resource will be cleaned up as soon as Dispose() is called rather than needing to wait for the type to be collected. If no one calls Dispose() (which is likely your colleague's concern), by the time your new type can be collected the instance of the original type for the unmanaged resource you are wrapping should be able to be collected as well, and it's finalizer will release the resource.

The main thing you need to bring to the table is that the finalizer cannot be called until the object is collected. You have to wait on the garbage collector, meaning you may be holding the resource open even longer. IDisposable allows you to release it right away. Of course you could do both, but that doesn't sound like what's going on here, and if you do have both you have to be careful not to conflict with the original type's finalizer or you could cause unwanted and harmful exceptions. And really, your own finalizer implementation is just redundant here and adds needless complexity.

Finally, I have to take issue with this statement:

If there is still a reference to the memory it will not be garbage collected

There can be references to an object and it will still be collected. What matters is if the object is reachable: are any of the references to the object rooted. For example, you may have a list with several objects in it. The list goes out of scope. Obviously there is still a reference to all of the objects in the list, but they can still all be collected in the first pass of the GC because the reference is no longer rooted.


1.6.7.6 of the Spec says:

1.6.7.6 Destructors

A destructor is a member that implements the actions required to destruct an instance of a class. Destructors cannot have parameters, they cannot have accessibility modifiers, and they cannot be invoked explicitly. The destructor for an instance is invoked automatically during garbage collection.

The garbage collector is allowed wide latitude in deciding when to collect objects and run destructors. Specifically, the timing of destructor invocations is not deterministic, and destructors may be executed on any thread. For these and other reasons, classes should implement destructors only when no other solutions are feasible.

The using statement provides a better approach to object destruction.

So no, it's not guaranteed they are called.


The only time that a finalizer won't be invoked at all is if an AppDomain is forcibly unloaded.
In general, you don't need to worry about it.


There is no guarantee.

There might be a guarantee if your process terminates nicely for some definition of nicely. But there are so many things not nice that can happen:

  • power failure
  • process terminated in a 'hard' or 'forced' way
  • unmanaged thread throwing calling OS exit() function or throwing an exception
  • call to System.Environment.FailFast, which does:

MSDN: "Terminates a process but does not execute any active try-finally blocks or finalizers."

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜