views:

1189

answers:

6

What kind of optimization patterns can be used to improve the performance of the garbage collector?

My reason for asking is that I do a lot of embedded software using the Compact Framework. On slow devices the garbage collection can become a problem, and I would like to reduce the times the garbage collector kicks in, and when it does, I want it to finish quicker. I can also see that working with the garbage collector instead of against it could help improve any .NET or Java application, especially heavy duty web applications.

Here are some of my thoughts, but I haven’t done any benchmarks.

  • reusing temporary classes/arrays (keep down allocation count)
  • keeping the amount of live objects to a minimum (faster collections)
  • try to use structs instead of classes
+2  A: 

One important fact is to keep the lifetime of your objects as short as possible.

VVS
Not necessarily. Lots of small, short lived objects will cause GC thrash way worse than just a few long-lived, large objects and would actually exacerbate his problem. This is *not* a desktop machine with desktop memory and processor resources
ctacke
I seem to remember the advice from MS is to keep objects as short lived as possible. Objects that survive generation 0 are more problematic.
Thomas Bratt
I found a reference to an MSDN article on this. Seems to be about a x10 slowdown per memory generation. The article is at: http://msdn.microsoft.com/en-us/magazine/dd882521.aspx#id0400035
Thomas Bratt
+2  A: 

The struct vs class issue is a complex one... you might easily end up using a lot more stack space, for example. And you certainly don't want mutable structs. But the other points seem sensible, as long as you aren't bending the design out of shape to accomodate it.

[edit] One other common gotcha is string concatenation; if you are doing concatenation in a loop, use StringBuilder, which will remove a lot of intermediate strings. It might be that GC is busy collecting all the abandoned vesions of your strings?

Marc Gravell
+2  A: 

Another option would be to manually collect the garbage during non-peak times in your application using GC.Collect() (assuming this is available in CF). This could reduce the objects required for cleanup later in your application.

JTA
A: 

I heard a .NET Rocks show on Rotor 2.0. If you are really hardcore, you could download Rotor, tweak the source, and use your own modified garbage collector.

In any case, that podcast has some great info on the GC. I highly recommend listening to it.

Jason Z
+3  A: 

The single most important aspect is to minimize the allocation rate. Whenever an object is allocated, it needs GC later. Now of course, if the object is small or shortlived it will get nailed in the young generation (provided that the GC is generational). Large objects tend to go directly into the tenured arena. But avoiding having to collect at all is even better.

Also, if you can throw things on the stack, you will enjoy much less pressure on the GC. You could attempt toying with GC-options, but I think you would be much better helped with an allocation profiler in hand, so you can find the spots that makes the problems.

The thing one should beware is the weight of standard libraries and frameworks. You wrap a couple of objects and it will fill up pretty quickly. Remember, whenever something goes on the GC-heap, it usually uses a bit more space for GC-bookkeeping. So your 1000 pointers allocated individually is much bigger than an array/vector of the same pointers since the latter can share the GC-bookkeeping. On the other hand, the latter will probably stay alive for much longer.

jlouis
The CF is non-generational - it's purely mark and sweep.
ctacke
+10  A: 

The key is to understand how the CF GC works for allocations. It's a simple mark-and-sweep, non-generational GC with specific algorithms for what will trigger a GC, and what will cause compaction and/or pitching after collection. There is almost nothing you can do at an app level to control the GC (the only method available is Collect, and it's use is pretty limited, as you can't force compaction anyway).

Object re-use is a good start, but simply keeping the object count low is probably one of the best tools, as all roots have to be walked for any collection operation. Keeping that walk short is a good idea. If compaction is killing you, then preventing segment fragmentation will help. Objects >64k can be helpful in that regard as they get their own segment and are treated differently than smaller objects.

To really understand how the CF GC works, I'd recommend watching the MSDN Webcast on CF memory management.

ctacke