Le réseau pour les développeurs >
Forums - Accueil
>
Windows Presentation Foundation (WPF)
>
FlowDocument Issue with Freeing Resources
FlowDocument Issue with Freeing Resources
- OVERVIEW:
Hello, I have recently came across an issue that has proven to be out of my league. I have narrowed it down to being something with a FlowDocument and/or TextRange. Basically, when I load an RTF file into a FlowDocument via TextRange.Load() it will not release all the resources (or whatever it is called) after all the references to these objects scope has run out. In my WPF application I load large RTF files as well as load multiple small ones repeatedly. Consequently this becomes a major issue and the memory usage for my application begins to become an overwhelming amount.
QUESTION:
My question is how can I fix this?
TRIED METHODS:
I have used used GC.Collect() as well as GC.WaitForPendingFinalizers() (recommended by someone). These work to a small degree but overall the issue still persists. I have also made sure no references are being made to anything, even the parent of the FlowDocument (FlowDocumentPageViewer).
Oh and another thing I tried was both Debug and non-Debug. Overall it doesn't make any difference.
TEST CODE:
Here is some test code that I have to test the problem.
Code Behind:
using System; using System.Collections.Generic; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; using System.IO; using System.Windows.Forms; namespace Testing { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void LoadRTF(object sender, RoutedEventArgs e) { System.Windows.Forms.OpenFileDialog openDlg = new OpenFileDialog(); openDlg.Filter = "Rich Text Format (*.rtf)|*.rtf"; openDlg.ShowDialog(); string path = openDlg.FileName; using (FileStream stream = new FileStream(path, FileMode.Open)) { TextRange range = new TextRange(richTextBox1.Document.ContentStart, richTextBox1.Document.ContentEnd); range.Load(stream, System.Windows.DataFormats.Rtf); } //make sure the GC collects everything GC.Collect(); GC.WaitForPendingFinalizers(); } private void OnClose(object sender, EventArgs e) { richTextBox1 = null; } } }
XAML:
<Window x:Class="Testing.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300" ShowInTaskbar="False" Closed="OnClose"> <Grid> <RichTextBox Name="richTextBox1" Margin="0,40,0,0" VerticalScrollBarVisibility="Visible" /> <Button Name="loadButton" VerticalAlignment="Top" Click="LoadRTF">Load RTF</Button> </Grid> </Window>
This code is something I quickly threw together for testing so I'm sure it is not optimal and also some of it is completely unnescessary. I was just testing out different things.
I had another window that would open up this window so that way I'd be sure that when I closed that window all the references inside it would be disposed of - at least that's what I would think...
Any help is greatly apprecaited. I have been attempting to solve this for coming up a week now and have not had any luck.
FINAL NOTES:
I am using WPF with .NET Framework 3.5 SP1 and VS2008 Professional.
I also asked this question on StackOverflow. You can find it here .
Toutes les réponses
- Hi Jasson
Usually when resources don't get freed it means not all references to them were cleaned. You'd need to do some memory usage analysis to find the problem. - I have tried this before but didn't come across anything. I just tried it again right now and still didn't really come across anything. I am unsure what I am really looking for.
I think that this is a fairly common problem given that it seems that just using TextRange.Load() causes it. I am still working on this...
Thanks,
~Jasson - What is the "UndoLimit" on the RichTextBox (default is -1 which allows the undo queue to use all available memory). Each time you load a new file into the RichTextBox, information is being saved to "undo" the edit (load). Depending on the limit, the amount of apparent memory leak could add up.
Les Potter, Xalnix Corporation, Yet Another C# Blog - TextRange.Load() and Undo do not cooperate very well, you should probably just turn it off before the load. Turn it back on after the load.
Les Potter, Xalnix Corporation, Yet Another C# Blog - Sorry, I lost track that this was about FlowDocument. So, try allocating a new flow document and replacing the old.
using (FileStream stream = new FileStream(path, FileMode.Open)) { flowdoc = new FlowDocument(); flow.Document = flowdoc; TextRange range = new TextRange(flowdoc.ContentStart, flowdoc.ContentEnd); range.Load(stream, System.Windows.DataFormats.Rtf); }
<Grid> <Grid.RowDefinitions> <RowDefinition Height="20"/> <RowDefinition /> </Grid.RowDefinitions> <Button Grid.Row="0" Click="LoadRTF">Clickme</Button> <FlowDocumentPageViewer Grid.Row="1" Name="flow" > <FlowDocument Name="flowdoc"></FlowDocument> </FlowDocumentPageViewer> </Grid>
Les Potter, Xalnix Corporation, Yet Another C# Blog - Changing the RichTextBox Undo isn't doing anything to the memory at least it is not the problem.
I also tried your second suggestion but used a RichTextBox instead of a FlowDocumentPageViewer since that is what I am wanting to use in my application. This as well does not seem to have any affect on the memory. Thank you for the suggestion.
~Jasson - Hello, I hate to bump this question but I am still looking for a solution. Any help is greatly appreciated. :)
- I'd suggest to investigate this issues under deubgger. Please have a look at DumpHeap related suggestion on this thread - http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/2245ee83-6f51-4095-9ed2-d82bbeda61a8
Let me know how it goes.
On a side note - closing the first window does not guarantee that all resources it referenced will be collected, since they still may be referenced from other places, depending on the logic of application. - Alright. I made an even simpler project to test this DumpHeap suggestion. This simple project only has a button inside the window. This is the click event for the button.
Using WinDbg, I clicked on the button a few (4) times and then checked the heap for both RichTextBox and FlowDocument types. Here is my log.private void Click_Button(object sender, EventArgs e) { RichTextBox b = new RichTextBox(); FlowDocument f = new FlowDocument(); }
0:007> .loadby sos mscorwks
0:007> !DumpHeap -type System.Windows.Controls.RichTextBox
Address MT Size
013817c4 55cdc0a0 280
013cfbcc 55cdc0a0 280
013dba0c 55cdc0a0 280
013e7864 55cdc0a0 280
total 4 objects
Statistics:
MT Count TotalSize Class Name
55cdc0a0 4 1120 System.Windows.Controls.RichTextBox
Total 4 objects
0:007> !DumpHeap -type System.Windows.Documents.FlowDocument
Address MT Size
013b7450 55cd21d8 84
013c6ec4 55cd21d8 84
013cfd4c 55cd21d8 84
013d3204 55cd21d8 84
013dbb8c 55cd21d8 84
013df054 55cd21d8 84
013e79e4 55cd21d8 84
013eae7c 55cd21d8 84
total 8 objects
Statistics:
MT Count TotalSize Class Name
55cd21d8 8 672 System.Windows.Documents.FlowDocument
Total 8 objects
I dont know if I am doing this correctly since I have never used WinDbg before, but if I am right it looks like both of those are not being released because they are being held by reference somewhere else. But where? I am not sure how how I should use this information to investigate further.
So my new questions are what is holding onto these and how can I go about releasing them? From what I understand this is a problem everyone has to deal with (or avoid). Thanks in advance :) - Interesting, how could there be 8 FlowDocuments if only 4 new operators were called:)
Is the Click_Button the only added code in application? Can you share whole repro app. solution to debug it? - If RichTextBox is not provided a FlowDocument when it is created then it will make its own. That is why there are twice as many FlowDocuments - this was expected this.
Here is all of the code in the project.
XAML
<Window x:Class="WpfApplication2.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300"> <Grid> <Button Click="Click_Button">Click</Button> </Grid> </Window>
Code Behind
using System; using System.Windows; using System.Windows.Controls; using System.Windows.Documents; namespace WpfApplication2 { /// <summary> /// Interaction logic for Window1.xaml /// </summary> public partial class Window1 : Window { public Window1() { InitializeComponent(); } private void Click_Button(object sender, EventArgs e) { RichTextBox b = new RichTextBox(); FlowDocument f = new FlowDocument(); } } }
That is all I added. Like I said, this is a really simple program but still has the problem. Try clicking on the button several times and watch the memory usage for this application go up and up. Thanks Hrach and good luck on your debugging attempt :) - I'm getting the same problem... I read a 140mb .rtf file into a TextRange, and memory sits at 240mb :( anyone solved this one yet?
- I have made a little progress in solving this but am still stuck. Here is a post I had on another site about this problem.
It was the first time I used Windbg and so I didn't know what to do with the address to find the references. Here is what I got.
Address MT Size
0131c9c0 55cd21d8 84
013479e0 55cd21d8 84
044dabe0 55cd21d8 84
total 3 objects
Statistics:
MT Count TotalSize Class Name
55cd21d8 3 252 System.Windows.Documents.FlowDocument
Total 3 objects
0:011> !gcroot 0131c9c0
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread 47c
Scan Thread 2 OSTHread be8
Scan Thread 4 OSTHread 498
DOMAIN(001657B0):HANDLE(WeakSh):911788:Root:0131ff98(System.EventHandler)->
0131fcd4(System.Windows.Documents.AdornerLayer)->
012fad68(MemoryTesting.Window2)->
0131c9c0(System.Windows.Documents.FlowDocument)
DOMAIN(001657B0):HANDLE(WeakSh):911cb0:Root:0131ca90(MS.Internal.PtsHost.PtsContext)->
0131cb14(MS.Internal.PtsHost.PtsContext+HandleIndex[])->
0133d668(MS.Internal.PtsHost.TextParagraph)->
0131c9c0(System.Windows.Documents.FlowDocument)
DOMAIN(001657B0):HANDLE(WeakSh):9124a8:Root:01320a2c(MS.Internal.PtsHost.FlowDocumentPage)->
0133d5d0(System.Windows.Documents.TextPointer)->
0131ca14(System.Windows.Documents.TextContainer)->
0131c9c0(System.Windows.Documents.FlowDocument)
This is after I closed the window. So it looks like it is being referenced by a few things. Now that I know this how do I go about freeing up these references so they can release the FlowDocument? - All the references above are weak references, so they shouldn't factor into keeping the FlowDocuments in memory. Has the GC actually run yet? If memory pressure is low, it won't be in any rush.
Two things in general that I can think of that might be at fault here, aside from an outright bug where you're accidently holding a reference directly or indirectly.
1. RichTextBox sometimes BeginInvokes a Dispatcher delegate to do some delayed initialization. Your test app will need to run the Dispatcher queue (Dispatcher.Run) to free this reference. If you're running with a default Application and just adding a handler for a Button press, this is already happening automatically.
2. The GC won't actually run until it detects sufficient memory pressure. Because RichTextBox has a finalizer, you need to be careful in how you force the collection:
GC.Collect();
GC.WaitingForPendingFinalizers();
GC.Collect();
Ben - Hey Ben, I am running a default application and just adding in a handler for the button press so that should be taken care of automagically I believe. As for #2, I have done this and still have the issue.
- I have the same problem.

