Solved

GC.SupressFinalize

Posted on 2009-04-04
15
635 Views
Last Modified: 2012-05-06
I am studying IDisposable and the Dispose pattern in .net and am a little unsure of some points.

When we call Dispose from client code we use:

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

So I am thinking about GC.SuppressFinalize(this) and what that does.  Well clearly it prevents this object from having its Finalize method run but then I am thinking what is it that the Finalize method does actually do?

The best understanding I can come up with so far is that Finalize is called just prior to an object being collected by the garbage collector and is an opportunity for us to hook into that part of the process and run Dispose(false).  Is there anything else which the Finalize method does or am I right in thinking it is just there for us to hook code into.

Hope this makes sense.

The code I am looking at is attached and I am not sure if the following bit is right:

                if (disposeManagedResources)
                {
                    filePath = null;
                    excelApplication = null;
                    excelWorkbook = null;
                }

Does this look right or have I misunderstood things?

Also I know the killing odf Excel is simplistic but it does fine for an example.
public SpreadSheet(string filePath, bool visible)

        {

            this.filePath = filePath;

 

            excelApplication = new Microsoft.Office.Interop.Excel.ApplicationClass();

 

            excelApplication.Visible = visible;

 

            excelWorkbook = excelApplication.Workbooks.Open(filePath, 0, false, 5, "password", "", true, Microsoft.Office.Interop.Excel.XlPlatform.xlWindows, "", true, false, 0, true, false, false);

        }

 

        public void DoSomeWork()

        {

            //This does some work with the spreadsheet - eg reading in some data and doing something with it

        }

 

        public void Dispose()

        {

            Dispose(true);

 

            GC.SuppressFinalize(this);

        }

 

        public void Dispose(bool disposeManagedResources)

        {

            if (!disposed)

            {

                foreach (System.Diagnostics.Process process in System.Diagnostics.Process.GetProcessesByName("EXCEL"))

                {

                    process.Kill();

                }

 

                if (disposeManagedResources)

                {

                    filePath = null;

                    excelApplication = null;

                    excelWorkbook = null;

                }

 

                disposed = true;

            }

        }

 

        ~SpreadSheet()

        {

            Dispose(false);

        }

    }

}

Open in new window

0
Comment
Question by:daveamour
  • 8
  • 7
15 Comments
 
LVL 3

Accepted Solution

by:
WeiXi earned 500 total points
ID: 24068086
The garbage collector does not call Dispose() by itself. It just calls the destructor immediately prior to releasing the memory where the object is living in. When, and it which order, objects are finalized/destructed by the garbage collection is arbitrary.

However, sometimes the release of resources or other clean-up tasks have to be triggered programmatically at specified points in time and in specific order. For this purpose exists the IDisposable interface, which provides a standard mechanism for releasing resources at a specified time.

So, in general, the tasks of the destructor and the Dispose() method do greatly or completely overlap. The difference is not what they do, but who calls them when. The rule of thumb is: If you don't care when the clean-up is performed then put it into the destructor. If you do care about it then put the clean-up into Dispose(). And, of course, call Dispose() from the destructor, as you do in your code example, because you can never _rely_ on Dispose() having been called.

Now to the question about GC.SuppressFinalize(this):
In most cases, like your example, it is not necessary for the garbage collector to call the destructor after Dispose() has been called already. It would do no harm, but it would do no good either. Thus, calling GC.SuppressFinalize(this) allows the garbage collector to optimize itself, since it knows it will never need to address the object again. As far as I know, garbage collector optimization is the only reason for SuppressFinalize().

Regarding your code, you got most of it right:
*) You are disposing only once.
*) You are disposing internal resources (other objects) only when called from Dispose(). When called from the destructor that is pointless.
*) You are disposing external resources (killing Excel processes) in any case.

However, three comments:
*) I am not very familiar with the Excel classes. If they have a Dispose() or Close() method, you should call it before setting them to null.
*) Setting a simple string variable (filePath) to null is rather pointless. It does do no harm, but I don't see a good reason for doing so. The Excel classes can potentially occupy a lot of memory, but a simple file path won't.
*) If you need your Excel resources for running DoSomeWork(), then it should start with
  if (disposed)
      throw new ObjectDisposedException("class name");
0
 
LVL 19

Author Comment

by:daveamour
ID: 24068519
Ok thanks that all makes sense and I think Ive got my head round it.
I'm not worried about how I am killing Excel here - that's just a crude example and I'm happy to use this for an example.
I've actually learned a bit more since posting this question too.  I've been working on an article about this for a litle while and hopefully got this about right.
If you have the time to read it I would appreciate it just to satisy myself that my understanding is right. Meanwhile thanks for the help.
My article is at  http://www.audacs.co.uk/ViewPage.aspx?PageID=497
0
 
LVL 3

Expert Comment

by:WeiXi
ID: 24068673
I quickly read through the article, and I think it's a fine piece of work. Thumbs up for taking your time to write articles like these for the public.

From a technical viewpoint I can't find any fault. However, as far as I am concerned, the readability becomes poor from this point on:
> After some thought I cannot find any easy way to explain what we need to do next
> so I am simply going to show you the final code for our Spreadsheet class and then
> explain what it is doing!

This criticism is probalby no surprise to you :)
I think it is possible to motivate that last section, i.e. give a a-priori reason to do "what we need to do next":

1) Motivate the disposing of managed resources. You mention the difference between managed and unmanaged resources at the beginning, but invoke the impression that managed ressources don't have to be cleaned up.

The most convincing example is the usage of a managed resource which, in turn, uses an external resource. Your Spreadsheet class could also facilitate a file stream which writes data from the excel sheet into a plain text file. Now, this text file needs to be freed as soon as possible as other processes might wait for it. Calling myStream.Dispose() is a much more convincing reason to handle a managed object in the Dispose method than setting myString=null :)

Furthermore the cascading of Dispose()-calls is a frequent pattern in C# programming, so it would be fine to introduce the reader to it.

I suggest, at this point you insert another code example like:
        public void Dispose()
        {
            if (!disposed)
            {
                foreach (System.Diagnostics.Process process in System.Diagnostics.Process.GetProcessesByName("EXCEL"))
                {
                    process.Kill();
                }
 
                    plainTextStream.Dispose();
                    excelApplication = null;
                    excelWorkbook = null;
                }
 
                disposed = true;
            }
        }

2) Now you can explain that cleaning up managed objects during the final garbage collection phase is pointless, and this motivates the need for the disposeManagedResources-flag.

One last comment:
The built in .net objects follow the convention, that Dispose() is public while Dispose(bool) is protected. In general, the only place from which Dispose(false) should be called is the destructor, no one outside the class should ever have the need to access Dispose(bool). Maybe you could change your code accordingly.
0
 
LVL 3

Expert Comment

by:WeiXi
ID: 24068706
Ah, a last-last comment:
Because you have written a tutorial-like article I'd like to re-suggest to include the following lines in your DoSomeWork() method:
    if (disposed)
          throw new ObjectDisposedException("class name");

For readers who are new to the subject this might be a valueable hint.
0
 
LVL 19

Author Comment

by:daveamour
ID: 24068771
Thank you that's excellent feedback and I will incorporate it all tomorrow and rewrite that last section.
Making the Dispose overload public was just a careless mistake so I will fix that.
I must admit I am still a little confused by:
                if (disposeManagedResources)
                {
                      //Clean up managed resources
                }
If client code did neglect to call Dispose then the destructor would call Dispose(false) and this bit above wouldn't be run and I'm not really sure I understand that.
Thanks very much.
 
0
 
LVL 3

Expert Comment

by:WeiXi
ID: 24068858
> If client code did neglect to call Dispose then the destructor would call Dispose(false)
> and this bit above wouldn't be run and I'm not really sure I understand that.

1) It is not necessary to call Dispose(true) from the destructor.

As you wrote in your article, the destructor is a fall-back mechanism in case the Dispose() method was not called from the code. Thus, when the destructor is called, timely disposal of resources is of no concern any more, that has already been messed up earlier by not calling Dispose().
Because the garbage collection will destroy _all_ managed objects anyway (and by doing so call their destructors which should do any important clean-up of their external resources), the destructor does not have to worry about any managed objects.

2) It would be very cumbersome to call Dispose(true) from the destructor.

The order of destruction by the garbage collection is not defined. At the time when the destructor is run, any managed object, which has been solely referenced by our garbage collected object, might have already been destroyed. That is no problem if you only say something like
   myString = null
If the former string object has already been destroyed, no one cares. But consider the example
   myFileStream.Dispose()
If you run this line of code during garbage collection and myFileStream has already been destroyed you run directly into an exception.

This becomes very obvious when you think of an example, where two IDisposable managed objects use each other as resource. However sophisticated the garbage collection is scheduling its destruction queue, one object will end up disposing an already freed resource.

In the end, you have to wrap each clean-up step of managed resources in a try-catch-block or put it into an if (objectToBeDisposed != null) { } block. Actually I am not sure whether the latter would work, because objectToBeDisposed has never been set to null. Wrapping each step into a try-catch would be very cumbersome to do and unruly costly in terms of run-time, especially considering point 1: It is not necessary to call Dispose(true) from the destructor.
0
 
LVL 19

Author Comment

by:daveamour
ID: 24070314
Ok thanks again

Do tell me to stop if I am asking too many questions but I can't let go of a thing until I understand it, I'm sure you know how that is!

So in your example regarding adding a stream or in fact any other managed objects which implement IDispobable, should we call their Dispose regardless of the value of the boolean parameter passed into Dispose(bool disposing)?

Eg:

        public void Dispose(bool dispoing)
        {
            if (!disposed)
            {
                foreach (System.Diagnostics.Process process in System.Diagnostics.Process.GetProcessesByName("EXCEL"))
                {
                    process.Kill();
                }
 
                plainTextStream.Dispose();

                if (disposing)
                {
                     excelApplication = null;
                     excelWorkbook = null;
                }
 
                disposed = true;
            }
        }

Such that the only clean up we are neglecting is that of 100% managed objects?

Assuming this is correct, would the garbage collector clean up excelApplication and excelWorkbook the next time it runs, or the time after?  When the garbage collector does run, then does it collect ALL unreferenced objects is what I am thinking in which case the excelApplication and excelWorkbook are referenced until the parent object is collected and so I was thinking they then migth not get collected till the next collection cycle? I can see that this is ok though as it just pure memory -  no open files etc.

Also should my Dispose overload be protected virtual, or just protected?

Thanks very much, I really can't say how much I appreciate your help, this is such a hard topic to get information on of the web!
0
IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

 
LVL 3

Expert Comment

by:WeiXi
ID: 24070862
> So in your example regarding adding a stream or in fact any other managed objects which
> implement IDispobable, should we call their Dispose regardless of the value of the boolean
> parameter passed into Dispose(bool disposing)?

No. To my understanding, the plainTextStream.Dispose() should be inside the if. It is clearly a managed resource:
                if (disposing)
               {
                    plainTextStream.Dispose();
                    excelApplication = null;
                    excelWorkbook = null;
               }

If Dispose() has not been called during object life-time, then let the garbage collection take care of the disposal. Timelyness is not an issue any more.

> When the garbage collector does run, then does it collect ALL unreferenced objects?

Well, there is not much information provided from MS on this question. All they say is that the time and the order of destruction is not defined and must not be relied on in any way. So, I would say the garbage collector strategy is propably more sophisticated and does take into account much more parameters, like e.g. using processor idle time.

> I was thinking they then migth not get collected till the next collection cycle?
> I can see that this is ok though as it just pure memory

If it's only about memory then leave the worries for the garbage collection. If memory is low then the garbage collector will start the next collection cycle immediately.

Actually, the whole point (well, a major point) about this garbage collection is that you don't have to worry about memory issues. I have the feeling you are starting to think along the lines of malloc() and free() of the standard C. The garbage collection relieves us from the need to worry about freeing managed objects and lets us concentrate on external/unmanaged ressources only. We should accept this offer and not try to do the job of the garbage collector.

> I really can't say how much I appreciate your help
I am happy to assist and hope it does your article good. I am also gaining more insight into this topic by discussing it with you.
0
 
LVL 19

Author Comment

by:daveamour
ID: 24071015
Ok thanks, think I get it all now.
I shall do some reworking of my article and let you have a look at that when it's done.
Thanks again.
 
0
 
LVL 3

Expert Comment

by:WeiXi
ID: 24071055
Ah, forgot about this:
> Also should my Dispose overload be protected virtual, or just protected?

Definitely protected virtual. I missed that point in your code, too.
Anything else would be rather useless. Any child class can potentially introduce new external resources, thus it must be possible to polymorphically overwrite the Dispose(). After all, the reliable freeing of external ressources must not rely on having no type cast when using the object.

I also remember an explicit statement in some MS knowledge base article, that calling base.Dispose() inside ones own Dispose() is a highly-recommended design pattern. Makes perfect sense to me, the child object only takes care of its own resources and leaves the base class resources to the base class.
0
 
LVL 19

Author Comment

by:daveamour
ID: 24071074
Ok thanks, so much to remember!
0
 
LVL 19

Author Comment

by:daveamour
ID: 24071110
Sorry 1 more question!
I am reading Wrox Professional C# and on page 223 it says that all managed resources will be cleaned up when code exits.
I just give the book and page in case you have it.
Anyway I know the garbage collector runs on a seperate thread with a higher priority given when it is collectting but are we saying that each process has its own version of a garbage collector running in one of it's threads and therefore when that process terminates then a final collection for that process will be issued?
Or is there some kind of permanent garbage collector managing all processes.
I'm thinking it is my first suggestion but just wanted to check.
Thanks
0
 
LVL 3

Expert Comment

by:WeiXi
ID: 24071163
I have to admit I am not 100% sure, but as far as I know, each and every .net application is running in its own CLR-process. Thus also its own garbage collection.
0
 
LVL 19

Author Comment

by:daveamour
ID: 24071168
Yep that makes sense and in all of my tests all of my collections have always occurred at the latest when the process ends.
I'll assume it is the case but try and do some research too.
Thanks again.
 
0
 
LVL 19

Author Comment

by:daveamour
ID: 24075498
Hi
Finished rewriting my article.
A quick review if you have time would be greatly appreciated.
Thanks
Dave
0

Featured Post

IT, Stop Being Called Into Every Meeting

Highfive is so simple that setting up every meeting room takes just minutes and every employee will be able to start or join a call from any room with ease. Never be called into a meeting just to get it started again. This is how video conferencing should work!

Join & Write a Comment

Suggested Solutions

Article by: Ivo
C# And Nullable Types Since 2.0 C# has Nullable(T) Generic Structure. The idea behind is to allow value type objects to have null values just like reference types have. This concerns scenarios where not all data sources have values (like a databa…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now