views:

446

answers:

4

In my application, I have a lot of RichTextBoxes that are created dynamically in run time. I realized that the application has a memory leak, which is caused by the RichTextBox controls. To prove that the memory leaks because of the control I wrote the following test method:

for (int i = 0; i < 3000; i++)
        {
            Control rich = new RichTextBox();
            pnl.Content = rich;
        }
GC.Collect();
GC.WaitForPendingFinalizers();

pnl is a ContentControl that is declared in Xaml code.
If you run the following code, you can see that the memory usage is growing rapidly.

Any ideas how to solve the problem? I considered creating an object pool, but this would complicate my application, and I rather avoid it.


edit: I've added call to the garbage collector to demonstrate that the objects are not garbage collected - there is no improvement in the memory usage with and without the call to the GC collect method. Note that calling rich.Dispose within the loop eliminates the memory usage growth.

+6  A: 

This isn't an indication that your application has a memory leak, it's an indication that your application is using a lot of memory. It's a leak if the RichTextBox controls don't get released at some point after they've fallen out of scope (detecting memory leaks on managed objects is notoriously difficult and unprovable).

A common misconception that an object falling out of scope will cause it to be garbage collected. This just makes it eligible to be collected. The object may theoretically never be collected until the application terminates. The fact that it grows with RichTextBox and not with other controls is not an indication that there is a memory leak in the RichTextBox, it's just an indication that it uses more memory per instance than other controls. While this information may be useful, it doesn't help when determining whether or not there's a memory leak.

Adam Robinson
My thoughts exactly.
Jason Punyon
Have you tried running the test code? The memory usage grows rapidly to the point in which the entire computer slows down. You can try the code with whatever WPF control, and you'll see that no significant memory growth occur. Note that the control is falling out of scope when replaced by a new control
Elad
@Elad: I've added to my answer to be a little more clear.
Adam Robinson
Forcing garbage collection doesn't change the memory usage (see my edit).
Elad
A: 

We had the same problem in winforms 2.0 and we had to buy a 3rd party rich text control. I guess that Microsoft didn't bother to fix it...

Ronny
I'm pretty sure there is no implementation resemblance between the Winforms and the WPF versoin of RichTextBox
ArielBH
A: 

Regarding your edit:

You've added the GC calls after the loop terminates and all 3000 RichTextBoxes have been created.

While I agree that it seems odd that the previous one isn't freed inside the loop when it's replaced by a new one, it's such a tight loop that the GC probably isn't getting a chance to "do it's stuff" before the loop terminates.

I don't think it's a good idea (but it should be OK in test code), but have you tried moving the GC calls inside the loop?

ChrisF
I tried your suggestion, but it doesn't seem to have any effect.
Elad
@Elad - oh well. It seemed like a reasonable suggestion.
ChrisF
A: 

Found this elsewhere, and it seems to be correct as far as my testing is concerned.

When a FlowDocument is created, relatively expensive formatting context objects are also created for it in its StructuralCache. When you create multiple FlowDocs in a tight loop, a StructuralCache is created for each FlowDoc. Let's you called Gc.Collect at the end of the loop, hoping to recover some memory. StructuralCache has a finalizer releases this formatting context, but not immediately. The finalizer effectively schedules an operation to release contexts at DispatcherPriority.Background.

So the RichTextBox (or FlowDocument) in case is not leaking just waiting on a background thread to cleanup. When it runs exactly who knows. I wish this just implemented a dispose method that would force a cleanup immediately.

Kelly
Original post: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/9cba6b80-c402-4f89-b300-6a9f8e4d7e37/
Elad