CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Steve Hebert's Development Blog

Steve's Blog - From .Net to dotMath and everything in between.

(Amazing) Code Smell - GC.Collect()

I was reading Jeremy Miller's post on Code Smells and another classic .Net code smell occurred to me.  (I have to say I love the term 'Code Smell' - I use it all the time now. Thanks Jeremy.)

The most insidious .Net coding smell I've run across is:
GC.Collect()

If you search your application and find this call, stop everything you are doing and focus on this problem.  Go check for this problem, I've seen it happen in runtime environments at 4 different companies so far. I recently talked to one programmer who bashed the .Net garbage collection performance because he said the model was to simply consume memory until it runs out of it.  Guess what - once again "GC.Collect()" was being called.

From what I've seen, GC.Collect typically gets injected into applications when memory resources aren't released.  Rather than understanding the root problem, GC.Collect gets thrown into the mix. The patch of running  GC.Collect() in your application will allow your application to run longer, but the memory allocation changes to a progressive stairstep effect where the process eventually hangs. 

The Garbage Collector is doing it's job correctly, however it's the routine and predictable calling of the collector that forces normally transient objects to be promoted to a status of "less transient" (my term, not theirs).  If you are calling this within your application you are introducing the same memory allocation path to the compiler each time and the same objects get promoted.  This promotion process is the normal functioning of garbage collection that allows the garbage collector to perform more efficiently and prevents the system from hanging for minutes on end like the old BASICA garbage collector.  Given this behavior and the articles I've read on the subject, I believe garbage collection in a framework environment like .Net and Java is predicated on running at random intervals to the application - allowing for a random distribution of errantly identified 'non-transient" objects. This also helps explain the need for multiple levels of promotion.

If you run into memory allocation problems within your application, resist the decision to inject GC.Collect() at all cost.  Go out and buy a tool like Red-Gates ANTS Profiler for .NET and take the time to understand the root problem.  If you go the GC.Collect route, you will typically delay the problem until production at which time profiling will include side-effects of the GC.Compiler call and will delay the understanding of the core issues even longer when the cost is greatest.





Comments

darrell said:

It's standard human behavior to blame the "thing I don't understand" for the problem, instead of my not understanding.

REAL developers say "I don't know", and then go find out instead of applying band-aids.
# July 21, 2005 8:51 AM

shebert said:

Great point Darrell.

I wonder if there is ever a point where GC.Collect should be called? I would think you'd need to architect your application around the need to prevent the promotion on commonly used objects. But then I'd think something like multi-threading creates another layer of runtime complexity that must be understood (if that's even possible in many situations).
# July 21, 2005 9:33 AM

Brendan Tompkins said:

Steve,

I actually did do some manual garbage collection using GC.Collect() within some code that wrote WMV movies on the fly. In this case, Calling GC.Collect manually cut the server memory used during this particluar method call in half.

Not saying the code wasn't smelly, it probably was.. But I had to make a decision between doing something that worked - that I didn't fully understand, and doing something that I understood perfectly which crashed my servers.


# July 21, 2005 10:46 AM

shebert said:

Hi Brendan,

That sounds like a cool project and a necessary use of the garbage collector.

I wonder if MS (or anyone else) has ever published any useful guidelines or rules on when, why and how you should call the garbage collector? I've never seen anything like that.
# July 21, 2005 11:47 AM

Christopher Steen - Learning .NET said:


(Amazing) Code Smell - GC.Collect()
BizTalk 2006 Beta Released
Continuous Integration for Visual...
# July 21, 2005 9:06 PM

Christopher Steen - Learning .NET said:


(Amazing) Code Smell - GC.Collect()
BizTalk 2006 Beta Released
Continuous Integration for Visual...
# July 21, 2005 9:15 PM

Christopher Steen - Learning .NET said:


(Amazing) Code Smell - GC.Collect()
BizTalk 2006 Beta Released
Continuous Integration for Visual...
# July 21, 2005 9:18 PM

Peter K said:

I ran into a similar issue with creating sometimes quite large crystal reports in a web application. While the objects were local and should have been released quickly, the memory taken by the app was growing rapidly.

We placed a GC.Collect() after using the close and dispose methods on the crystal objects and saw much better memory usage patterns.

"Sometimes you gotta do what you gotta do."
# July 22, 2005 6:13 AM

Colin Kershaw said:

FYI - the idea of "code smells" was introduced by Kent Beck in Chapter 3 of "Refactoring" by Martin Fowler. Check out "Refactoring" for the list of code smells and solutions they've compiled:

* Duplicated Code
* Long Method
* Large Class
* Long Parameter List
* Divergent Change
* Shotgun Surgery
* Feature Envy
* Data Clumps
* Primitive Obsession
* Switch Statements
* Parallel Inheritance Hierarchies
* Lazy Class
* Speculative Generality
* Temporary Field
* Message Chains
* Middle Man
* Inappropriate Intimacy
* Alternative Classes with Different Interfaces
* Incomplete Library Class
* Data Class
* Refused Bequest
* Comments
# July 25, 2005 8:52 AM

shebert said:

Check out Maoni's blog at http://blogs.msdn.com/maoni/default.aspx for a four part discussion on using the GC efficiently. The blog hasn't been updated in a long time, but the material is excellent. (Thanks to Raymond Lewellen for the link!)
# July 26, 2005 11:07 AM

Steve Hebert's Development Blog said:

I previously blogged about a set must-read garbage collection articles and issues around directly...
# July 26, 2005 2:52 PM

Eric Newton said:

Peter K, you have a decent use of GC.Collect()

Normally, that call is made after certain thresholds anyway. Your call simply "encourages" the GC to clean up all those temp objects Crystal Reports builds will building up a report for output. I wouldn't normally embed a GC.Collect() in a class library though. The best place is "right at the edge" meaning, the page or the Form (in Winforms) So Peter's case I would put the GC.Collect in the Page.Unload or Page.Render as the last call.

In a Winforms app, consider a form with a lot of objects bound to listviews/comboboxes and so forth... upon closing the form, that's a lot of temp objects floating. And especially since its a single user scenario, this is a great time to GC.Collect on the Form's Closed event handler.

Those are the only two times I would ever call GC.Collect() otherwise, just let the GC determine memory usage by itself... it does have tuning characteristics that adjust to more dynamic situations.

# July 29, 2005 1:41 AM

Rob Caron's Blog - A Team System Nexus said:

Cleaning-out my “To Blog” file again…
Architects

Handling data in service oriented systemsEdward...
# August 1, 2005 2:58 AM
Check out Devlicio.us!

Our Sponsors

Proudly Partnered With