Introduction
This is yet another article on the use of the interface class IDisposable. Essentially, the code that you are going to see here is no different than the code on MSDN or these two excellent articles:
Garbage Collection in .NET by Chris Maunder:
http://www.codeproject.com/managedcpp/garbage_collection.asp
General Guidelines for C# Class Implementation by Eddie Velasquez:
http://www.codeproject.com/csharp/csharpclassimp.asp
What's different is that this article illustrates the functional aspects of:
- the destructor
- the
Dispose method
- memory allocation
- garbage collection issues
In other words, there's lots of articles showing how to implement IDisposable, but very few demonstration of why to implement IDisposable.
How To Implement IDisposable
The salient features of the code below are:
- Implement the
Dispose method of the IDisposable interface
- Only
Dispose of resources once
- The implementing class requires a destructor
- Prevent the GC from disposing of resources if they've already been manually disposed
- Track whether the GC is disposing of the object rather than the application specifically requesting that the object is disposed. This concerns how resources that the object manages are handled.
And here's a flowchart:

Note two things:
- If
Dispose is used on an object, it prevents the destructor from being called and manually releases managed and unmanaged resources.
- If the destructor is called, it only releases unmanaged resources. Any managed resources will be de-referenced and also (possibly) collected.
There are two problem with this, which I'll come back to later:
- Using Dispose does not prevent you from continuing to interact with the object!
- A managed resource may be disposed of, yet still referenced somewhere in the code!
Here's an example class implementing IDisposable, which manages a Image object and has been instrumented to illustrate the workings of the class.
public class ClassBeingTested : IDisposable
{
private bool disposed=false;
private Image img=null;
public Image Image
{
get {return img;}
}
public ClassBeingTested()
{
Trace.WriteLine("ClassBeingTested: Constructor");
}
~ClassBeingTested()
{
Trace.WriteLine("ClassBeingTested: Destructor");
Dispose(false);
}
public void Dispose()
{
Trace.WriteLine("ClassBeingTested: Dispose");
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposeManagedResources)
{
if (!this.disposed)
{
Trace.WriteLine("ClassBeingTested: Resources not disposed");
if (disposeManagedResources)
{
Trace.WriteLine("ClassBeingTested: Disposing managed resources");
if (img != null)
{
img.Dispose();
img=null;
}
}
Trace.WriteLine("ClassBeingTested: Disposing unmanaged resouces");
disposed=true;
}
else
{
Trace.WriteLine("ClassBeingTested: Resources already disposed");
}
}
public void LoadImage(string file)
{
Trace.WriteLine("ClassBeingTested: LoadImage");
img=Image.FromFile(file);
}
}
Why Implement IDisposable?
Let's put this class into a simple GUI driven test fixture that looks like this:

and we'll use two simple tools to monitor what's going:
The test is very simple, involving loading a 3MB image file several times, with the option to dispose of the object manually:
private void Create(int n)
{
for (int i=0; i<n; i++)
{
ClassBeingTested cbt=new ClassBeingTested();
cbt.LoadImage("fish.jpg");
if (ckDisposeOption.Checked)
{
cbt.Dispose();
}
}
}
The unsuspecting fish, by the way, is a Unicorn Fish, taken at Sea World, San Diego California:

Observe what happens when I create 10 fish:

Ten fish took up 140MB, (which is odd, because the fish is only a 3MB file, so you'd think no more than 30MB would be consumed, but we won't get into THAT).
Furthermore, observe that the destructors on the objects were never invoked:

If we create 25 fish, followed by another 10, notice what happens to the time it takes to haul in the fish, as a result of heavy disk swapping:

This is now taking two seconds on average to load one fish! And did the GC start collecting garbage any time soon? No! Conversely, if we dispose of the class as soon as we're done using it (which in our test case is immediately), there is no memory hogging and no performance degradation. So, to put it mildly, it is very important to consider whether or not a class needs to implement the IDispose interface, and whether or not to manually dispose of objects.
Behind The Scenes
Let's create one fish and then force the GC to collect it. The resulting trace looks like:

Observe that in this case, the destructor was called and managed resources were not manually disposed.
Now, instead, let's create one fish with the dispose flag checked, then force the GC to collect it. The resulting trace looks like:

Observe in this case, that both managed and unmanaged resources are disposed, AND that the destructor call is suppressed.
Problems
As described above, even though an object is disposed, there is nothing preventing you from continuing to use the object and any references you may have acquired to objects that it manages, as demonstrated by this code:
private void btnRefTest_Click(object sender, System.EventArgs e)
{
ClassBeingTested cbt=new ClassBeingTested();
cbt.LoadImage("fish.jpg");
Image img=cbt.Image;
cbt.Dispose();
Trace.WriteLine("Image size=("+img.Width.ToString()+", "+img.Height.ToString()+")");
}
Of course, the result is:

Solutions
Ideally, one would want to assert or throw an exception when:
Dispose is called and managed objects are still referenced elsewhere
- methods and accessors are called after the object has been disposed
Unfortunately (as far as I know) there is no way to access the reference count on an object, so it becomes somewhat difficult to determine if a managed object is still being referenced elsewhere. Besides require that the application "release" references, the best solution is to simply not allow another object to gain a reference to an internally managed object. In other words, the code:
public Image Image
{
get {return img;}
}
should simply not be allowed in a "managing" class. Instead, the managing class should implement all the necessary support functions that other classes require, implementing a facade to the managed object. Using this approach, the application can throw an exception if the disposed flag is true--indicating that the object is still being accessed after it has technically been disposed of.
Conclusion - Unit Testing
The reason I went through this rigmarole is that I wanted to demonstrate the inadequacies of unit testing. For example, let us assume that the test class I described above does not implement IDisposable. Here we have an excellent example of how a single test on a class and its functions will succeed wonderfully, giving the illusion that all is well with a program that uses the class. But all is not well, because the class does not provide a means for the application to dispose of its managed resources, ultimately causing the entire computer system to bog down in fragmented memory and disk swapping.
This does not mean that unit testing is bad. It does however illustrate that it is far from a complete picture, and unit testing applications such as NUnit could use considerable growth in order to help the programmer automate more complex forms of unit testing.
And that, my friends, is going to be the topic of the next article.
Downloading The Demonstration Project
I have intentionally left out the "fish.jpg", being 3MB in size. Please edit the code and use your own JPG if you wish to play with the code.
| You must Sign In to use this message board. |
|
|
 |
|
 |
Hi Mr. Clifton,
In your article above, you wrote:
Ten fish took up 140MB, (which is odd, because the fish is only a 3MB file, so you'd think no more than 30MB would be consumed, but we won't get into THAT).
I recently ran into the same thing (while loading Data Tables into a Data Grid View control). I quickly exceeded WinXP's 2 GB per user limit, whereas if I simply dumped the data for all of these tables into Excel documents, the total file sizes came out to 25 MB or so.
Could you shed some light on what's going on? It doesn't have to be MVP exact verbiage, but I am curious about what is happening.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
jp2code wrote: Could you shed some light on what's going on?
My take on it is that DataTables are horribly inefficient, and a DataGridView is probably doing a lot of indexing behind the scenes, as is a DataView, to support fast sorting and filtering. And Excel is probably doing some sort of compression.
Marc
Will work for food.Interacx I'm not overthinking the problem, I just felt like I needed a small, unimportant, uninteresting rant! - Martin Hart Turner
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi Marc,
I use DataTables often. I never heard anyone say they were inefficient. Not knocking your take, just wondering why you say that. You could be dead on!
Could you provide some details? If they are that bad, I need to stop using them in my Pocket PC applications!
Thanks, Joe
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Good article. It does show the basis of idispose. the referance should still be left to the programmer and unit test as you outlined.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
/// /// Method for cleanning up cosmetic memory usage /// This for ensure the application always use small memory for life time /// /// System.Diagnostics.Process.GetCurrentProcess().Handle /// -1 /// -1 /// [System.Runtime.InteropServices.DllImport("Kernel32", EntryPoint = "SetProcessWorkingSetSize", ExactSpelling = true, CharSet = CharSet.Ansi, SetLastError = true)] private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize); //private extern static Boolean CloseHandle(IntPtr handle);
// Dispose(bool disposing) executes in two distinct scenarios. // If disposing equals true, the method has been called directly // or indirectly by a user's code. Managed and unmanaged resources // can be disposed. // If disposing equals false, the method has been called by the // runtime from inside the finalizer and you should not reference // other objects. Only unmanaged resources can be disposed. /// /// Method for Cleanup Resource /// /// protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. if(!this.disposed) { // If disposing equals true, dispose all managed // and unmanaged resources. if(disposing) { // Dispose managed resources. component.Dispose(); } // Release unmanaged resources. If disposing is false, // only the following code is executed. GC.Collect(); GC.WaitForPendingFinalizers(); if (Environment.OSVersion.Platform == PlatformID.Win32NT) { SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1); } // Note that this is not thread safe. // Another thread could start disposing the object // after the managed resources are disposed, // but before the disposed flag is set to true. // If thread safety is necessary, it must be // implemented by the client.
} disposed = true; } // Implement IDisposable. // Do not make this method virtual. // A derived class should not be able to override this method. /// /// IDispose Method /// public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); }
in the destructor area u can use Dispose(false)
this way will be free your memory alot, i got this code from someone also just try to help
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Hi. Thanx for the article.. One quick question.. the following code block.. public void Dispose() { Trace.WriteLine("ClassBeingTested: Dispose"); // dispose of the managed and unmanaged resources Dispose(true);
// tell the GC that the Finalize process no longer needs // to be run for this object. GC.SuppressFinalize(this); }
protected virtual void Dispose(bool disposeManagedResources) { // process only if mananged and unmanaged resources have // not been disposed of. if (!this.disposed) { 1. Trace.WriteLine("ClassBeingTested: Resources not disposed"); if (disposeManagedResources) { Trace.WriteLine("ClassBeingTested: Disposing managed resources"); // dispose managed resources if (img != null) { img.Dispose(); img=null; } } // dispose unmanaged resources Trace.WriteLine("ClassBeingTested: Disposing unmanaged resouces"); disposed=true; } else { Trace.WriteLine("ClassBeingTested: Resources already disposed"); } }
Is it valid to call the GC.SuppressFinalize(this); where it is. if the code fails (for what ever reason) on the line line marked 1. The local variable will not be set and the resources not cleared up, BUT the GC is told to supress it.
Thanx
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Did the output for the sample come from the Debug Window or DebugView. I ran DebugView and didn't see anything relating to app running. At least form what I could see.
56 29.97971393 [3636] ClassBeignTested: Constructor ..... .... ...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
(I'm using .Net framework 1.0 on Win2K)
I have implemented a pop3 application in ASP.Net.. which downloads mail from a POP server and updates the DB on click of a button in the page.
Whenever i download large mails (of 5 MB or more) ...the memory consumption of the process 'aspnet_wp.exe' increases dramatically (like 300 MB, which is greater than 60% of the available RAM - 512MB) and is finally recycled by the OS... which results in the download being aborted.
Below i have given the piece of code which actually retrieves the data from the POP server. Please review it suggest me as to why it happens. The memory consumption increases when the processing enters this piece of code.
int BUFFERSIZE = 100000; char [] buffer = new char[BUFFERSIZE]; int len; string strb = ""; try { string prevcur,szTemp,prev; szTemp = RdStrm.ReadLine(); prevcur = prev = szTemp+"\r\n"; was_pop_error(szTemp); if(!error) { while(prevcur.IndexOf("\r\n.\r\n") == -1) { strb += prev; len = 0; len = RdStrm.Read(buffer,0,BUFFERSIZE); string cur = new System.Text.StringBuilder("").Append(buffer,0,len).ToString(); prevcur = prev+cur; prev = cur; } } prevcur = prevcur.Replace("\r\n.\r\n",""); strb += prevcur; return(strb); } catch(InvalidOperationException err) { return("Error in mail download" + err.ToString ()); }
Vikram S
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I would suggest using the string builder for prevcur and cur. The line "prevcur=prev+cur" will probably be very inefficient as it results in .NET creating a new string, rather than expanding the current one.
Marc
Microsoft MVP, Visual C#
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Marc Clifton wrote: I would suggest using the string builder for prevcur and cur.
I tried using the stringbuilder, but the memory consumption remains the same. For a mail of size 3MB, the memory consumption of aspnet_wp.exe process increases from abt 50Mb to 150 MB when executing these lines of code.
Also the mempory is never released when the download is complete even though i have inherited the class from IDisposable and doing all that is necessary in the dispose method. I had commented the line, for testing, where the stingbuilder is used to append the data obtained from the stream and found that the aspnet_wp.exe process never consumes anymore than 55MB while executing these lines of code.
Vikram
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Blue Tender wrote: I tried using the stringbuilder, but the memory consumption remains the same. For a mail of size 3MB, the memory consumption of aspnet_wp.exe process increases from abt 50Mb to 150 MB when executing these lines of code.
Hmmm. I'm out of ideas. Try posting this problem on the ASP.NET forum. Maybe a couple other ASP.NET sites as well.
Marc
Microsoft MVP, Visual C#
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
you are using the strings temporarily, they get re created with each append (+). these are not class members and thus they constantly create garbage that does not dealt with by implementing IDisposable.
what you should have done is use StringBuilder extensively rather than temporarily. StringBuilder.ToString(), should have only been called in the return statement.
use string builders instead of strings for prev, curr and
this is what focussed programmers do
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I can explain that...a 3MB jpeg is compressed, and once loaded into memory it is represented as a bitmap, i.e. much more memory. In your case, it looks like JPEG compression crunched it down to about 20-25% of its original size, which is consistent with what JPEG is capable of.
If you rerun your tests with a 3MB .bmp file, you might see something closer to what you expected.
I'm sure it wasn't keeping anyone up at night, but I thought I'd mention it for all the obsessive types
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
LOL I was just about to post the same thing. 
JPEG = compressed. In memory, it's uncompressed! As Steve points out, to see the REAL amount of memory needed, save this as an uncompressed file format. It might even be a bit larger depending on the capabilities of the object-- alpha blending requires an alpha bit, etc.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
On my machine (Win2k, 256MB RAM, .NET-Framework 1.1) the test has another kind of problem: the GC doesn't fire at all (unless it is called explicitely). Even if the machine runs out of memory the GC doesn't fire. Therefore the Reason of the Load-Image-Delay is the disk-swapping and not the GC. It may be that the GC fires only if a certain amount of managed Memory was allocated; so I modified the test adding some menaged-memory allocation (strings):
private System.Collections.Specialized.StringCollection sc;
private void Create(int n) { for (int i=0; i { ClassBeingTested cbt=new ClassBeingTested(); cbt.LoadImage("fish.jpg"); cbt.FillStrings(15); if (ckDisposeOption.Checked) { cbt.Dispose(); } } }
public void FillStrings(int n) { if( sc == null ) sc = new System.Collections.Specialized.StringCollection(); string s = "qwert"; for(int i=0; i { s += "_"+s; sc.Add(s); } }
Now the GC fires and even witout the call of Dispose the application behaves well. Therefore it seems to me that the explicite call of Dispose has not such an important impact as it may seem from your test-program.
Richard
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
This is now taking two seconds on average to load one fish! And did the GC start collecting garbage any time soon? No!
I agree with you but WHY ?
There is only one true reference to image; all the other references are not accessible. Do you know the real reason behind this?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I did some more tests, and finally got the GC to collect on its own only when my machine was starting to run out of memory (this happened after creating the image about 10,000 times).
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
I'm not a Windows forms programmer, so this is just a guess, but my thinking is that the GC doesn't come around to collect those until it's necessary (as when you started to run out of memory), or perhaps when you exit the program. In ASP.NET the GC comes around after the page is destroyed, which is right after you've sent it off to the browser.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
howcheng wrote: but my thinking is that the GC doesn't come around to collect those until it's necessary (as when you started to run out of memory), or perhaps when you exit the program. In ASP.NET the GC comes around after the page is destroyed, which is right after you've sent it off to the browser.
Both are correct. The GC doesn't come around until it's necessary or when the app exits. But there is also, I believe, some intelligence, in that, if .NET detects that the system is idle, it starts doing cleanup on its own.
What bugs me about this is that it's not really under my control. It's like the cartoon where the pilot hits the "landing gear down" button, and the computer responds with "please wait for garbage collection".
The reason the GC comes around after the page is destroyed is because literally the web application is effectively exited and isn't reloaded until a post-back occurs. But that's a good point--in ASP.NET, the issues of GC are less of a potential problem.
Marc
Latest AAL Article My blog Join my forum!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
"in ASP.NET, the issues of GC are less of a potential problem."
However, that is not always true. We have developed a system that is all ASP.NET and we are having major memory problems. (See below)
Also you mentioned, "that it's (GC) not really under my control". You can however call System.GC.Collect(); or System.GC.Collect(2); and that will run the GC. If you want to just call System.GC.Collect(0); it will clean up the generation 0 memory and leave all others.
The problems that we have has is that we run out of memory and the GC does not run in time to free the memory up before it just stops responding altogether. We have something like 6 gigs of memory and 4 processors. The GC allows the memory to get close to full then tries to free up memory just in time to lock up IIS. Sweet!
What we have done until we can make some changes is to restart IIS ever 3 hours. I know sweet!!! But temporarily that is what has to be done. Next we are going to try and call System.GC.Collect(); every hour instead of restarting IIS every 3 hours.
The rub is that we have destructors on everything but they don't seem to be called until GC runs. We run started running Dispose() now on several different objects. We will see how that works.
Thanks to everyone that posted!! And thanks for the article!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I notice that you and others on CP (e.g. Chris Maunder) have provided some example implementations of IDisposable that maintain a private member m_disposed for keeping track of whether the object has been disposed already.
Is this really necessary for most cases, as long as something does not revive the finalized object from a zombie state? I can see how it might be nice to keep track of, though.
Thanks, Arun
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Arun Bhalla wrote: I notice that you and others on CP
The code for the Dispose function is straight out of MSDN, and since Microsoft does it that way, we all do it that way.
:sheep noise:
But, it also prevents errors if the application accidentally calls the Dispose method more than once.
C# doesn't really solve the problem with allocated memory management, it just shifts it.
Marc
Latest AAL Article My blog Join my forum!
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Marc Clifton wrote:
The code for the Dispose function is straight out of MSDN, and since Microsoft does it that way, we all do it that way.
Heh, yes. I just hadn't noticed the new style until now. A handful of months ago, Microsoft had announced the previous style, which isn't too different.
Thanks, Arun
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|