What's so wrong about using GC.Collect()?

66,371

Solution 1

From Rico's Blog...

Rule #1

Don't.

This is really the most important rule. It's fair to say that most usages of GC.Collect() are a bad idea and I went into that in some detail in the orginal posting so I won't repeat all that here. So let's move on to...

Rule #2

Consider calling GC.Collect() if some non-recurring event has just happened and this event is highly likely to have caused a lot of old objects to die.

A classic example of this is if you're writing a client application and you display a very large and complicated form that has a lot of data associated with it. Your user has just interacted with this form potentially creating some large objects... things like XML documents, or a large DataSet or two. When the form closes these objects are dead and so GC.Collect() will reclaim the memory associated with them...

So it sounds like this situation may fall under Rule #2, you know that there's a moment in time where a lot of old objects have died, and it's non-recurring. However, don't forget Rico's parting words.

Rule #1 should trump Rule #2 without strong evidence.

Measure, measure, measure.

Solution 2

If you call GC.Collect() in production code you are essentially declaring that you know more then the authors of the GC. That may be the case. However it's usually not, and therefore strongly discouraged.

Solution 3

So how about when you are using COM objects like MS Word or MS Excel from .NET? Without calling GC.Collect after releasing the COM objects we have found that the Word or Excel application instances still exist.

In fact the code we use is:

Utils.ReleaseCOMObject(objExcel)

' Call the Garbage Collector twice. The GC needs to be called twice in order to get the
' Finalizers called - the first time in, it simply makes a list of what is to be finalized,
' the second time in, it actually does the finalizing. Only then will the object do its 
' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, 
' but one that may be necessary when automating Excel because it is the only way to 
' release all the Excel COM objects referenced indirectly.
' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5
' Ref: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()

So would that be an incorrect use of the garbage collector? If so how do we get the Interop objects to die? Also if it isn't meant to be used like this, why is the GC's Collect method even Public?

Solution 4

Well, the GC is one of those things I have a love / hate relationship with. We have broken it in the past through VistaDB and blogged about it. They have fixed it, but it takes a LONG time to get fixes from them on things like this.

The GC is complex, and a one size fits all approach is very, very hard to pull off on something this large. MS has done a fairly good job of it, but it is possible to fool the GC at times.

In general you should not add a Collect unless you know for a fact you just dumped a ton of memory and it will go to a mid life crisis if the GC doesn't get it cleaned up now.

You can screw up the entire machine with a series of bad GC.Collect statements. The need for a collect statement almost always points to a larger underlying error. The memory leak usually has to do with references and a lack of understanding to how they work. Or using of the IDisposable on objects that don't need it and putting a much higher load on the GC.

Watch closely the % of time spent in GC through the system performance counters. If you see your app using 20% or more of its time in the GC you have serious object management issues (or an abnormal usage pattern). You want to always minimize the time the GC spends because it will speed up your entire app.

It is also important to note that the GC is different on servers than workstations. I have seen a number of small difficult to track down problems with people not testing both of them (or not even aware that their are two of them).

And just to be as full in my answer as possible you should also test under Mono if you are targeting that platform as well. Since it is a totally different implementation it may experience totally different problems that the MS implementation.

Solution 5

There are situations where it's useful, but in general it should be avoided. You could compare it to GOTO, or riding a moped: you do it when you need to, but you don't tell your friends about it.

Share:
66,371

Related videos on Youtube

Trap
Author by

Trap

I'm a software developer working for a videogame company.

Updated on September 26, 2020

Comments

  • Trap
    Trap over 3 years

    Although I do understand the serious implications of playing with this function (or at least that's what I think), I fail to see why it's becoming one of these things that respectable programmers wouldn't ever use, even those who don't even know what it is for.

    Let's say I'm developing an application where memory usage varies extremely depending on what the user is doing. The application life cycle can be divided into two main stages: editing and real-time processing. During the editing stage, suppose that billions or even trillions of objects are created; some of them small and some of them not, some may have finalizers and some may not, and suppose their lifetimes vary from a very few milliseconds to long hours. Next, the user decides to switch to the real-time stage. At this point, suppose that performance plays a fundamental role and the slightest alteration in the program's flow could bring catastrophic consequences. Object creation is then reduced to the minimum possible by using object pools and the such but then, the GC chimes in unexpectedly and throws it all away, and someone dies.

    The question: In this case, wouldn't it be wise to call GC.Collect() before entering the second stage?

    After all, these two stages never overlap in time with each other and all the optimization and statistics the GC could have gathered would be of little use here...

    Note: As some of you have pointed out, .NET might not be the best platform for an application like this, but that's beyond the scope of this question. The intent is to clarify whether a GC.Collect() call can improve an application's overall behaviour/performance or not. We all agree that the circumstances under which you would do such a thing are extremely rare but then again, the GC tries to guess and does it perfectly well most of the time, but it's still about guessing.

    Thanks.

    • Steve Jessop
      Steve Jessop over 15 years
      "the slightest alteration in the program's flow could bring catastrophic consequences ... someone could die" - are you sure C# .NET is sufficiently deterministic for your purposes?
    • Sergio Acosta
      Sergio Acosta over 15 years
      Not Windows nor .NET are realtime platforms and therefore you cannot guarantee performance metrics, at least not enough to the point of risking human lives. I agree with onebyone that either you are exaggerating or careless.
    • The Dag
      The Dag over 11 years
      LOL at "one of these things that respectable programmers wouldn't ever use, even those who don't even know what it is for"! Programmers who use stuff not knowing why are hardly the most respectable in my book. :)
  • Scott Dorman
    Scott Dorman over 15 years
    Not only do you cause the stack walk, but your applications main thread (and any child threads it created) are frozen so the GC can walk the stack. The more time your app spends in GC, the more time it spends frozen.
  • TraumaPony
    TraumaPony over 15 years
    He could be using the .Net Micro Framework, which WAS designed for real-time environments.
  • Scott Dorman
    Scott Dorman over 15 years
    @TraumaPony: Check the chart at the bottom of this page msdn.microsoft.com/en-us/embedded/bb278106.aspx: Clearly the Micro Framework was not designed for real-time environments. It was, however, designed for embedded environments (like WinCE) but with lower power requirements.
  • Trap
    Trap over 15 years
    The heuristic nature of the garbage collector and the fact that they exposed this functionality to the outside world make me think of it as something that is useful if used where it needs to be. The problem is not using it but knowing how, where and when to use it.
  • Ashwin
    Ashwin over 15 years
    Interesting, but the documentation says that AddMemoryPressure should be used when 'a small managed object allocates a large amount of UNmanaged memory'. (emphasis mine)
  • Jorge Córdoba
    Jorge Córdoba almost 15 years
    I'd say this is just the old thing. Nothing is really bad or dangerous if you know what you're doing and therefore know when and how to do it, as well as it side effects. Things like don't never, ever use xxxx are put there to protect the world from lousy programmers :D
  • Ian Ringrose
    Ian Ringrose over 14 years
  • MickyD
    MickyD over 12 years
    thats perhaps a generalisation
  • MasterMastic
    MasterMastic over 11 years
    That's very true, but I don't know if they could make assumptions that apply for all developments.
  • Andrew Barber
    Andrew Barber over 11 years
    Calling Dispose is not supposed to release managed memory. You don't seem to know how the memory model in .NET works.
  • rkagerer
    rkagerer over 11 years
    This would make a great new StackOverflow question, i.e: How to eradicate COM instances without calling the GC. With particular regard to unmanaged circular references. It's one of the challenges that made me wary of upgrading my VB6 Outlook add-in to C#. (We did a lot of work to develop coding patterns and test cases on the VB side that guaranteed COM references were killed in a deterministic fashion when no longer needed).
  • The Dag
    The Dag over 11 years
    @Ken No, they can't. But are you in a better position to do so? Or are you going to write code assuming specific hardware, a specific OS version and so on? The pain/gain ratio is too high on this one.
  • The Dag
    The Dag over 11 years
    The culprit is often events. Whenever an instance method is used as an event handler, the publisher of the event has a reference to the subscriber via the event delegate. The only "easy" way to avoid problems from this is to only use publishers that are at most as long-lived as the subscribers (e.g. a TextBox publishing an event handled by the containing form is no problem, as the textbox isn't supposed to live outside of the form). Example problem scenario: Singleton model, temporary views handling model events.
  • The Dag
    The Dag over 11 years
    If this applies to COM objects in general, perhaps this is a valid scenario. But off the bat I'd say the problem is likely that you are using a client application designed for an interactive desktop as a COM server. From MSDN knowledge base: "Microsoft does not currently recommend, and does not support, Automation of Microsoft Office applications from any unattended, non-interactive client application or component (including ASP, ASP.NET, DCOM, and NT Services), because Office may exhibit unstable behavior and/or deadlock when Office is run in this environment."
  • The Dag
    The Dag over 11 years
    Not to mention the GCs better knowledge about every other application and their memory needs. The GC negotiates memory with the OS and as such is impacted by available physical memory and all other processes on the machine both managed and unmanaged. While I doubt the GC really knows "when is a good time to collect" on a "case-by-case" basis, it's very likely to have a better strategy overall than... ANY single application. ;)
  • MasterMastic
    MasterMastic over 11 years
    @TheDag IMO of course I am. When I'm releasing memory and whatnot I don't really care about the hardware because that's the OS job to deal with that. I also don't care about the OS because I have an interface common to all of the ones I'm programming for. (e.g. I don't care if it's Windows, Mac, or Linux: when I'm allocating/freeing memory in C/C++ it's new/delete malloc/dealloc). I could always be wrong so feel free to correct me.
  • Dib
    Dib over 9 years
    @TheDag - Microsoft may not recommend, but many of us have had to port old VB6 code with with office interop to .Net windows apps. I spent months of work until I finally got rid rid of all invisible hanging references for a big VB6 to .Net conversion project. Learning to release in reverse assignment order and holding local refs to EVERY single com object including the collections helped though.
  • Wonderbird
    Wonderbird almost 9 years
    I am more concerned about an App Crash due to an Out of Memory exception than slow performance because the app / GC was throwing out things that are no longer needed. Does anybody know why Microsoft appears to throw an OOM exception without FIRST throwing out the garbage? (Without this OBVIOUS step - or at least an explanation of why this step does not appear to be attempted before throwing OOM exception I'm not sure I have any faith in things happening "automatically" the "way they are supposed to."
  • Adam R. Grey
    Adam R. Grey almost 8 years
    How can one screw up the entire machine?
  • Luaan
    Luaan about 7 years
    @MasterMastic malloc only has a very simple interface, and it's implementations can vary enough to matter. It all depends on what kind of a problem you're trying to solve. If malloc was "good enough", you wouldn't need buffer pooling, would you? C/C++ development is full of examples where you try to second-guess the OS/runtime/libraries because you know better (and sometimes, you really do). Many performance-critical applications avoid using system/runtime allocators entirely. Games used to pre-allocate all memory at startup (constant-sized arrays etc.).
  • Silent Sojourner
    Silent Sojourner over 6 years
    I'm not saying using GC.Collect is good practice. But sometimes it is a quick way to resolve issues without knowing its real cause. It's ugly, I know, but it does work, and it seems to me not a bad approach, especially when there's not much time to figure out the root cause of the issue and your boss is standing behind you... you know.