Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>The point of Dispose <strong>is</strong> to free unmanaged resources. It needs to be done at some point, otherwise they will never be cleaned up. The garbage collector doesn't know <strong>how</strong> to call <code>DeleteHandle()</code> on a variable of type <code>IntPtr</code>, it doesn't know <strong>whether</strong> or not it needs to call <code>DeleteHandle()</code>.</p> <blockquote> <p><strong>Note</strong>: What is an <em>unmanaged resource</em>? If you found it in the Microsoft .NET Framework: it's managed. If you went poking around MSDN yourself, it's unmanaged. Anything you've used P/Invoke calls to get outside of the nice comfy world of everything available to you in the .NET Framwork is unmanaged – and you're now responsible for cleaning it up.</p> </blockquote> <p>The object that you've created needs to expose <em>some</em> method, that the outside world can call, in order to clean up unmanaged resources. The method can be named whatever you like: </p> <pre><code>public void Cleanup() public void Shutdown() </code></pre> <p>But instead there is a standardized name for this method:</p> <pre><code>public void Dispose() </code></pre> <p>There was even an interface created, <code>IDisposable</code>, that has just that one method:</p> <pre><code>public interface IDisposable { void Dispose() } </code></pre> <p>So you make your object expose the <code>IDisposable</code> interface, and that way you promise that you've written that single method to clean up your unmanaged resources:</p> <pre><code>public void Dispose() { Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); } </code></pre> <p>And you're done. <strong>Except you can do better.</strong></p> <hr> <p>What if your object has allocated a 250MB <strong><a href="http://msdn.microsoft.com/en-us/library/system.drawing.bitmap.aspx" rel="noreferrer">System.Drawing.Bitmap</a></strong> (i.e. the .NET managed Bitmap class) as some sort of frame buffer? Sure, this is a managed .NET object, and the garbage collector will free it. But do you really want to leave 250MB of memory just sitting there – waiting for the garbage collector to <em>eventually</em> come along and free it? What if there's an <a href="http://msdn.microsoft.com/en-us/library/system.data.common.dbconnection.aspx" rel="noreferrer">open database connection</a>? Surely we don't want that connection sitting open, waiting for the GC to finalize the object.</p> <p>If the user has called <code>Dispose()</code> (meaning they no longer plan to use the object) why not get rid of those wasteful bitmaps and database connections?</p> <p>So now we will:</p> <ul> <li>get rid of unmanaged resources (because we have to), and </li> <li>get rid of managed resources (because we want to be helpful)</li> </ul> <p>So let's update our <code>Dispose()</code> method to get rid of those managed objects:</p> <pre><code>public void Dispose() { //Free unmanaged resources Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //Free managed resources too if (this.databaseConnection != null) { this.databaseConnection.Dispose(); this.databaseConnection = null; } if (this.frameBufferImage != null) { this.frameBufferImage.Dispose(); this.frameBufferImage = null; } } </code></pre> <p>And all is good, <strong>except you can do better</strong>!</p> <hr> <p>What if the person <strong>forgot</strong> to call <code>Dispose()</code> on your object? Then they would leak some <strong>unmanaged</strong> resources! </p> <blockquote> <p><strong>Note:</strong> They won't leak <strong>managed</strong> resources, because eventually the garbage collector is going to run, on a background thread, and free the memory associated with any unused objects. This will include your object, and any managed objects you use (e.g. the <code>Bitmap</code> and the <code>DbConnection</code>).</p> </blockquote> <p>If the person forgot to call <code>Dispose()</code>, we can <em>still</em> save their bacon! We still have a way to call it <em>for</em> them: when the garbage collector finally gets around to freeing (i.e. finalizing) our object.</p> <blockquote> <p><strong>Note:</strong> The garbage collector will eventually free all managed objects. When it does, it calls the <strong><code>Finalize</code></strong> method on the object. The GC doesn't know, or care, about <em>your</em> <strong>Dispose</strong> method. That was just a name we chose for a method we call when we want to get rid of unmanaged stuff.</p> </blockquote> <p>The destruction of our object by the Garbage collector is the <em>perfect</em> time to free those pesky unmanaged resources. We do this by overriding the <code>Finalize()</code> method. </p> <blockquote> <p><strong>Note:</strong> In C#, you don't explicitly override the <code>Finalize()</code> method. You write a method that <em>looks like</em> a <strong>C++ destructor</strong>, and the compiler takes that to be your implementation of the <code>Finalize()</code> method:</p> </blockquote> <pre><code>~MyObject() { //we're being finalized (i.e. destroyed), call Dispose in case the user forgot to Dispose(); //&lt;--Warning: subtle bug! Keep reading! } </code></pre> <p>But there's a bug in that code. You see, the garbage collector runs on a <strong>background thread</strong>; you don't know the order in which two objects are destroyed. It is entirely possible that in your <code>Dispose()</code> code, the <strong>managed</strong> object you're trying to get rid of (because you wanted to be helpful) is no longer there:</p> <pre><code>public void Dispose() { //Free unmanaged resources Win32.DestroyHandle(this.gdiCursorBitmapStreamFileHandle); //Free managed resources too if (this.databaseConnection != null) { this.databaseConnection.Dispose(); //&lt;-- crash, GC already destroyed it this.databaseConnection = null; } if (this.frameBufferImage != null) { this.frameBufferImage.Dispose(); //&lt;-- crash, GC already destroyed it this.frameBufferImage = null; } } </code></pre> <p>So what you need is a way for <code>Finalize()</code> to tell <code>Dispose()</code> that it should <strong>not touch any managed</strong> resources (because they <em>might not be there</em> anymore), while still freeing unmanaged resources.</p> <p>The standard pattern to do this is to have <code>Finalize()</code> and <code>Dispose()</code> both call a <strong>third</strong>(!) method; where you pass a Boolean saying if you're calling it from <code>Dispose()</code> (as opposed to <code>Finalize()</code>), meaning it's safe to free managed resources.</p> <p>This <em>internal</em> method <em>could</em> be given some arbitrary name like "CoreDispose", or "MyInternalDispose", but is tradition to call it <code>Dispose(Boolean)</code>:</p> <pre><code>protected void Dispose(Boolean disposing) </code></pre> <p>But a more helpful parameter name might be:</p> <pre><code>protected void Dispose(Boolean itIsSafeToAlsoFreeManagedObjects) { //Free unmanaged resources Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //Free managed resources too, but only if I'm being called from Dispose //(If I'm being called from Finalize then the objects might not exist //anymore if (itIsSafeToAlsoFreeManagedObjects) { if (this.databaseConnection != null) { this.databaseConnection.Dispose(); this.databaseConnection = null; } if (this.frameBufferImage != null) { this.frameBufferImage.Dispose(); this.frameBufferImage = null; } } } </code></pre> <p>And you change your implementation of the <code>IDisposable.Dispose()</code> method to:</p> <pre><code>public void Dispose() { Dispose(true); //I am calling you from Dispose, it's safe } </code></pre> <p>and your finalizer to:</p> <pre><code>~MyObject() { Dispose(false); //I am *not* calling you from Dispose, it's *not* safe } </code></pre> <blockquote> <p><strong>Note</strong>: If your object descends from an object that implements <code>Dispose</code>, then don't forget to call their <strong>base</strong> Dispose method when you override Dispose:</p> </blockquote> <pre><code>public Dispose() { try { Dispose(true); //true: safe to free managed resources } finally { base.Dispose(); } } </code></pre> <p>And all is good, <strong>except you can do better</strong>!</p> <hr> <p>If the user calls <code>Dispose()</code> on your object, then everything has been cleaned up. Later on, when the garbage collector comes along and calls Finalize, it will then call <code>Dispose</code> again. </p> <p>Not only is this wasteful, but if your object has junk references to objects you already disposed of from the <strong>last</strong> call to <code>Dispose()</code>, you'll try to dispose them again! </p> <p>You'll notice in my code I was careful to remove references to objects that I've disposed, so I don't try to call <code>Dispose</code> on a junk object reference. But that didn't stop a subtle bug from creeping in.</p> <p>When the user calls <code>Dispose()</code>: the handle <strong>CursorFileBitmapIconServiceHandle</strong> is destroyed. Later when the garbage collector runs, it will try to destroy the same handle again.</p> <pre><code>protected void Dispose(Boolean iAmBeingCalledFromDisposeAndNotFinalize) { //Free unmanaged resources Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //&lt;--double destroy ... } </code></pre> <p>The way you fix this is tell the garbage collector that it doesn't need to bother finalizing the object – its resources have already been cleaned up, and no more work is needed. You do this by calling <code>GC.SuppressFinalize()</code> in the <code>Dispose()</code> method:</p> <pre><code>public void Dispose() { Dispose(true); //I am calling you from Dispose, it's safe GC.SuppressFinalize(this); //Hey, GC: don't bother calling finalize later } </code></pre> <p>Now that the user has called <code>Dispose()</code>, we have:</p> <ul> <li>freed unmanaged resources</li> <li>freed managed resources</li> </ul> <p>There's no point in the GC running the finalizer – everything's taken care of.</p> <h2>Couldn't I use Finalize to cleanup unmanaged resources?</h2> <p>The documentation for <a href="https://msdn.microsoft.com/en-us/library/system.object.finalize.aspx" rel="noreferrer"><code>Object.Finalize</code></a> says:</p> <blockquote> <p>The Finalize method is used to perform cleanup operations on unmanaged resources held by the current object before the object is destroyed.</p> </blockquote> <p>But the MSDN documentation also says, for <a href="https://msdn.microsoft.com/en-us/library/system.idisposable.dispose(v=vs.110).aspx" rel="noreferrer"><code>IDisposable.Dispose</code></a>:</p> <blockquote> <p>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</p> </blockquote> <p>So which is it? Which one is the place for me to cleanup unmanaged resources? The answer is: </p> <blockquote> <p>It's your choice! But choose <code>Dispose</code>.</p> </blockquote> <p>You certainly could place your unmanaged cleanup in the finalizer:</p> <pre><code>~MyObject() { //Free unmanaged resources Win32.DestroyHandle(this.CursorFileBitmapIconServiceHandle); //A C# destructor automatically calls the destructor of its base class. } </code></pre> <p>The problem with that is you have no idea when the garbage collector will get around to finalizing your object. Your un-managed, un-needed, un-used native resources will stick around until the garbage collector <em>eventually</em> runs. Then it will call your finalizer method; cleaning up unmanaged resources. The documentation of <strong>Object.Finalize</strong> points this out:</p> <blockquote> <p>The exact time when the finalizer executes is undefined. To ensure deterministic release of resources for instances of your class, implement a <strong>Close</strong> method or provide a <a href="https://msdn.microsoft.com/en-us/library/system.idisposable.dispose(v=vs.110).aspx" rel="noreferrer"><code>IDisposable.Dispose</code></a> implementation.</p> </blockquote> <p>This is the virtue of using <code>Dispose</code> to cleanup unmanaged resources; you get to know, and control, when unmanaged resource are cleaned up. Their destruction is <em>"deterministic"</em>.</p> <hr> <p>To answer your original question: Why not release memory now, rather than for when the GC decides to do it? I have a facial recognition software that <em>needs</em> to get rid of 530 MB of internal images <strong>now</strong>, since they're no longer needed. When we don't: the machine grinds to a swapping halt.</p> <h2>Bonus Reading</h2> <p>For anyone who likes the style of this answer (explaining the <em>why</em>, so the <em>how</em> becomes obvious), I suggest you read Chapter One of Don Box's Essential COM: </p> <ul> <li>Direct link: <a href="http://ptgmedia.pearsoncmg.com/images/0201634465/samplechapter/chap01.pdf" rel="noreferrer">Chapter 1 sample by Pearson Publishing</a></li> <li>magnet: 84bf0b960936d677190a2be355858e80ef7542c0</li> </ul> <p>In 35 pages he explains the problems of using binary objects, and invents COM before your eyes. Once you realize the <em>why</em> of COM, the remaining 300 pages are obvious, and just detail Microsoft's implementation.</p> <p>I think every programmer who has ever dealt with objects or COM should, at the very least, read the first chapter. It is the best explanation of anything ever.</p> <h2>Extra Bonus Reading</h2> <p><a href="https://ericlippert.com/2015/05/18/when-everything-you-know-is-wrong-part-one/" rel="noreferrer">When everything you know is wrong</a> by Eric Lippert</p> <blockquote> <p>It is therefore very difficult indeed to write a correct finalizer, and <strong>the best advice I can give you is to not try</strong>.</p> </blockquote>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload