breeve said:
There is a memory leak here (run the code in Framework 2.0). We have seen it
in our code base (running GC.Collect doesn't help). It was reported by one of
our customers using our product. I ran SOS and you can plainly see the leak.
It consists of 16 byte WeakReference objects and lots of them. We have also
run DevPartner (Memory Analysis) and see the same leak. Just run the code and
you will see your memory climb over time.
A memory leak is memory that is never reclaimed; not memory that is
reclaimed slowly.
Connecting the dots in my previous message: When the SolidBrush is
collected, the WeakReference in the SystemColorTracker will go stale.
When you set 'enough' solid brushes to system colors, the
SystemColorTracker list will hit max capacity, and delete stale weak
references. That's two long cycles, and the interaction does mean that
the SystemColorTracker will hold weak references for a long time.
Even if you create your brushes within a `using` statement, so that
they get finalized as soon as you're done with them, they will stick
around until the next garbage collection. At that point, the weak
reference will go stale; next time the SystemColorTracker list fills
up, the weak reference will be delinked. Even then, the weak reference
will last until the next garbage collection; since the weak reference
has already survived a gen 0 collection, it will last until the next
gen 1 collection, while if the weak reference has already survived a
gen 1 collection, it will last until the next gen 2 collection. Gen 1
collections are rare, and gen 2 collections are even rarer. So, those
SystemColorTracker weak references will last for a long time - even
with SystemColorTracker at default capacity. If the SystemColorTracker
has had to increase its capacity, the whole cycle takes even longer.
Of course, if you don't create your brushes within a `using` statement
(or manually dispose of them), things take even longer than that! Now
the unreferenced brush hangs around, with a valid GDI+ handle, until
the next garbage collection. At that point, the gc treats it as live
data (promotes the brush to the next generation, and relocates it) and
adds it to the finalization queue. Now the brush won't get reclaimed
until the next (rare) gen 1 or gen 2 collection. When that finally
happens, the weak reference will go stale, and be delinked when the
SystemColorTracker next fills up. Of course, now the weak reference is
in gen 2, and so it will stick around for a very long time, too.
Bottom line: The behavior you and your customer are seeing is 100% in
line with what one would expect from the FCL code. Lots of very long
lived, very small objects.
Take away message: Using system colors with solid brushes has a
certain amount of overhead, in terms of small, long-lived objects.
Creating your solid brushes in a `using` statement will minimize this
overhead.