FlowDocument Never Completes Loading
- I'm working on an MSDN Forums Browser (http://forumsbrowser.codeplex.com/) and I'm running into a bit of a performance issue.
As part of the browser, I'm using the FlowDocumentScrollViewer to display generated FlowDocuments containing the contents of each thread. In doing so, I'm using the BlockUIContainer in conjunction with an HTML to XAML converter I found online.
Unfortunately, when my app attempts to load a very large thread (approximately 30 messages or over), the app stops working properly, and the Memory footprint of the App simply continues to rise. On the UI, I can see the beginning of the
There doesn't seem to be an issue generating the FlowDocument, as that happens rather quickly. The issue, however, appears to happen as soon as I assign the generated document to the Document property of the FlowDocumentScrollViewer. At this point, I start getting my issues, and when I say the memory continues rising... it just continues rising, at about 3-4 megs per second. I've seen it get up to around 2 gigs of memory at one point.
Any ideas? Here's the code I'm using currently. It's a bit mangled at the moment, because I've been trying to fix the issue.
public void ViewThread(Thread thread) { IsWebBrowser = false; this.Tag = thread.Url; // By the way, "Thread" in this context refers to a forums thread, not a System.Threading.Thread. // I've used Fully qualified names for System.Threading items. thread.Update(thread.IsComplete ? thread : Gateway.ThreadGet(thread.XmlUrl)); try { System.Threading.ThreadStart ts = () => { var op = browser.Dispatcher.BeginInvoke(new Action(() => { var doc = GetNewFlowDocument(thread); // generate the document. This works fine. browser.Document = null; // feeble attempt. GC.Collect(); // feeble attempt. browser.Document = doc; // here's where the UI stops. The execution continues, however. } ), System.Windows.Threading.DispatcherPriority.Background); op.Completed += new EventHandler(op_Completed); }; var t = new System.Threading.Thread(ts); t.Start(); } catch { // This is called when there's just an outright problem. This can happen when the format of the HTML snippet for a message contains // some wierd settings. MessageBox.Show("Oops! I won't be able to see this thread in thread view. I'll open it up for you in the web browser instead." + "\n\nIf you want this to be looked at, post an issue at http://forumsbrowser.codeplex.com/ and reference the following URL.\n\n" + thread.Url); this.Browse(thread); } }
David Morton - http://blog.davemorton.net/ - @davidmmorton
Answers
- This is a quick memory footprint of objects contributing most to the memory usage:
This log was done at some point (using windbg):
54d9692c 769 264536 System.Windows.Controls.TextBlock
61423150 1010 269640 System.Collections.Hashtable+bucket[]
6141fcbc 5159 288904 System.Reflection.RuntimeMethodInfo
004830b8 106 305176 Free
53aed4a4 50412 604944 System.Windows.Media.PenLineJoin
53acecf0 50398 806368 MS.Utility.SingleItemList`1[[System.Windows.Media.PathSegment, PresentationCore]]
53acee10 50401 806416 MS.Utility.SingleItemList`1[[System.Windows.Media.PathFigure, PresentationCore]]
613f41d0 15580 911728 System.Object[]
04134ad8 50461 1211064 MS.Utility.ThreeItemList`1[[System.Object, mscorlib]]
0412ee90 50756 1218144 System.Windows.Point
61420a00 24826 1588648 System.String
53ad98cc 50398 1612736 System.Windows.Media.PolyLineSegment
0412abcc 100798 1612768 MS.Utility.ArrayItemList`1[[System.Windows.Point, WindowsBase]]
53ace6ec 50401 1612832 System.Windows.Media.PathFigure
613f4590 103225 1651600 System.Double
61419c6c 52976 1695232 System.EventHandler
53aed3c8 151235 1814820 System.Windows.Media.PenLineCap
53af1914 51770 1863720 System.Windows.SizeChangedInfo
53af1700 50402 2016080 System.Windows.Media.PathFigureCollection
53ace86c 50402 2016080 System.Windows.Media.PathSegmentCollection
53ae9c68 50485 2019400 System.Windows.Media.Pen
61423470 51706 2673472 System.Byte[]
53af1b34 50708 3042480 System.Windows.Media.RenderData
53af1510 50401 3830476 System.Windows.Media.PathGeometry
53ae290c 100799 4031960 System.Windows.Media.PointCollection
041300ac 258857 10886508 System.Windows.EffectiveValueEntry[]
54db95c4 50400 12700800 System.Windows.Shapes.Polygon
04133a3c 103592 18394624 System.Windows.Point[]
Total 1801894 objects
This was done couple of seconds later:
54d9692c 769 264536 System.Windows.Controls.TextBlock
61423150 999 266760 System.Collections.Hashtable+bucket[]
6141fcbc 5158 288848 System.Reflection.RuntimeMethodInfo
004830b8 280 316696 Free
53aed4a4 68202 818424 System.Windows.Media.PenLineJoin
613f41d0 18486 978052 System.Object[]
53acecf0 68188 1091008 MS.Utility.SingleItemList`1[[System.Windows.Media.PathSegment, PresentationCore]]
53acee10 68191 1091056 MS.Utility.SingleItemList`1[[System.Windows.Media.PathFigure, PresentationCore]]
61420a00 24547 1576044 System.String
04134ad8 68133 1635192 MS.Utility.ThreeItemList`1[[System.Object, mscorlib]]
0412ee90 68546 1645104 System.Windows.Point
53ad98cc 68188 2182016 System.Windows.Media.PolyLineSegment
0412abcc 136379 2182064 MS.Utility.ArrayItemList`1[[System.Windows.Point, WindowsBase]]
53ace6ec 68191 2182112 System.Windows.Media.PathFigure
613f4590 139415 2230640 System.Double
61419c6c 72683 2325856 System.EventHandler
53aed3c8 204605 2455260 System.Windows.Media.PenLineCap
53af1914 69382 2497752 System.Windows.SizeChangedInfo
53af1700 68192 2727680 System.Windows.Media.PathFigureCollection
53ace86c 68192 2727680 System.Windows.Media.PathSegmentCollection
53ae9c68 68275 2731000 System.Windows.Media.Pen
61423470 70094 4063424 System.Byte[]
53af1b34 68380 4102800 System.Windows.Media.RenderData
53af1510 68191 5182516 System.Windows.Media.PathGeometry
53ae290c 136380 5455200 System.Windows.Media.PointCollection
041300ac 350554 14637312 System.Windows.EffectiveValueEntry[]
54db95c4 68191 17184132 System.Windows.Shapes.Polygon
04133a3c 143775 25211604 System.Windows.Point[]
Total 2399045 objects
For examnple there is a big number of String, Byte[] and Reflection.RuntimeMethodInfo objects, which keeps groing constantly. At least for the last one, I honestly don't think WPF would do this (are you using Reflection extensively?).
I'd suggest to do some memory profiling on the app. If you'd like to I can get a link to the WPF profiler tools as well.
Note also the growing number of EventHandlers.- Marked As Answer byDavid M MortonMVPThursday, June 11, 2009 11:09 PM
Okay, I've narrowed it down. The issue has to do with a specific control within the BlockUIContainer, namely the "MedalControl" as I call it. The purpose of the MedalControl is to show a visual representation of the user's medals on the screen. It is composed of five "Star" controls, that have a geometrically defined star shape. I was having some issues with rendering and rerendering, so I had added this code:
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);Star[] stars = new Star[] { One, Two, Three, Four, Five };
for (int i = 0; i < stars.Length; i++)
{
stars[i].Height = this.Height;
stars[i].Width = this.Width;
stars[i].Fill = this.FillBrush;
stars[i].Edge = this.Edge;
stars[i].Filled = Achieved > i;
}
}
I'm assuming this is a bad thing.
To make matters worse, the Filled setter was doing a whole slew of other things.
Anyways, to finish this out, it's still looking a bit funky, but at least it's not blowing up anymore.
Thanks for your tip about the event handlers. It seems like I had a latent stack overflow, but never got there because of the sheer number of things I was requiring the UI to do.
David Morton - http://blog.davemorton.net/ - @davidmmorton- Marked As Answer byDavid M MortonMVPThursday, June 11, 2009 11:09 PM
All Replies
- Hi David
First thing coming to the mind is possibility that some objects don't get collected because of held references. There were cases when not all references where cleaned-up due to references kept on event handlers.
Another question comes up regarding 'browser' object - is it a singleton intended to be reused in subsequent calls? I've seen some cases when captured variables don't get collected, because of the capturing anonymous methods don't get released. I'd suggest to double check that unnneded document objects get collected (for example with some additional traces; or you could use WinDbg with sos.dll extension and check number of objects of given type with DumpHeap -type). - The browser object is the FlowDocumentScrollViewer. I was under the assumption that I could simply reuse this.
There's nothing special about the event handlers as far as I know. I'm not attaching any to the document itself.
It's very reproducable. You could download the code from Codeplex, run it, browse to the C# Language Forum, and open the thread that is currently on the front page with 34 posts on it called "Enum Why should I use "break"...?!" and you'll see it die.
David Morton - http://blog.davemorton.net/ - @davidmmorton - This is a quick memory footprint of objects contributing most to the memory usage:
This log was done at some point (using windbg):
54d9692c 769 264536 System.Windows.Controls.TextBlock
61423150 1010 269640 System.Collections.Hashtable+bucket[]
6141fcbc 5159 288904 System.Reflection.RuntimeMethodInfo
004830b8 106 305176 Free
53aed4a4 50412 604944 System.Windows.Media.PenLineJoin
53acecf0 50398 806368 MS.Utility.SingleItemList`1[[System.Windows.Media.PathSegment, PresentationCore]]
53acee10 50401 806416 MS.Utility.SingleItemList`1[[System.Windows.Media.PathFigure, PresentationCore]]
613f41d0 15580 911728 System.Object[]
04134ad8 50461 1211064 MS.Utility.ThreeItemList`1[[System.Object, mscorlib]]
0412ee90 50756 1218144 System.Windows.Point
61420a00 24826 1588648 System.String
53ad98cc 50398 1612736 System.Windows.Media.PolyLineSegment
0412abcc 100798 1612768 MS.Utility.ArrayItemList`1[[System.Windows.Point, WindowsBase]]
53ace6ec 50401 1612832 System.Windows.Media.PathFigure
613f4590 103225 1651600 System.Double
61419c6c 52976 1695232 System.EventHandler
53aed3c8 151235 1814820 System.Windows.Media.PenLineCap
53af1914 51770 1863720 System.Windows.SizeChangedInfo
53af1700 50402 2016080 System.Windows.Media.PathFigureCollection
53ace86c 50402 2016080 System.Windows.Media.PathSegmentCollection
53ae9c68 50485 2019400 System.Windows.Media.Pen
61423470 51706 2673472 System.Byte[]
53af1b34 50708 3042480 System.Windows.Media.RenderData
53af1510 50401 3830476 System.Windows.Media.PathGeometry
53ae290c 100799 4031960 System.Windows.Media.PointCollection
041300ac 258857 10886508 System.Windows.EffectiveValueEntry[]
54db95c4 50400 12700800 System.Windows.Shapes.Polygon
04133a3c 103592 18394624 System.Windows.Point[]
Total 1801894 objects
This was done couple of seconds later:
54d9692c 769 264536 System.Windows.Controls.TextBlock
61423150 999 266760 System.Collections.Hashtable+bucket[]
6141fcbc 5158 288848 System.Reflection.RuntimeMethodInfo
004830b8 280 316696 Free
53aed4a4 68202 818424 System.Windows.Media.PenLineJoin
613f41d0 18486 978052 System.Object[]
53acecf0 68188 1091008 MS.Utility.SingleItemList`1[[System.Windows.Media.PathSegment, PresentationCore]]
53acee10 68191 1091056 MS.Utility.SingleItemList`1[[System.Windows.Media.PathFigure, PresentationCore]]
61420a00 24547 1576044 System.String
04134ad8 68133 1635192 MS.Utility.ThreeItemList`1[[System.Object, mscorlib]]
0412ee90 68546 1645104 System.Windows.Point
53ad98cc 68188 2182016 System.Windows.Media.PolyLineSegment
0412abcc 136379 2182064 MS.Utility.ArrayItemList`1[[System.Windows.Point, WindowsBase]]
53ace6ec 68191 2182112 System.Windows.Media.PathFigure
613f4590 139415 2230640 System.Double
61419c6c 72683 2325856 System.EventHandler
53aed3c8 204605 2455260 System.Windows.Media.PenLineCap
53af1914 69382 2497752 System.Windows.SizeChangedInfo
53af1700 68192 2727680 System.Windows.Media.PathFigureCollection
53ace86c 68192 2727680 System.Windows.Media.PathSegmentCollection
53ae9c68 68275 2731000 System.Windows.Media.Pen
61423470 70094 4063424 System.Byte[]
53af1b34 68380 4102800 System.Windows.Media.RenderData
53af1510 68191 5182516 System.Windows.Media.PathGeometry
53ae290c 136380 5455200 System.Windows.Media.PointCollection
041300ac 350554 14637312 System.Windows.EffectiveValueEntry[]
54db95c4 68191 17184132 System.Windows.Shapes.Polygon
04133a3c 143775 25211604 System.Windows.Point[]
Total 2399045 objects
For examnple there is a big number of String, Byte[] and Reflection.RuntimeMethodInfo objects, which keeps groing constantly. At least for the last one, I honestly don't think WPF would do this (are you using Reflection extensively?).
I'd suggest to do some memory profiling on the app. If you'd like to I can get a link to the WPF profiler tools as well.
Note also the growing number of EventHandlers.- Marked As Answer byDavid M MortonMVPThursday, June 11, 2009 11:09 PM
Okay, I've narrowed it down. The issue has to do with a specific control within the BlockUIContainer, namely the "MedalControl" as I call it. The purpose of the MedalControl is to show a visual representation of the user's medals on the screen. It is composed of five "Star" controls, that have a geometrically defined star shape. I was having some issues with rendering and rerendering, so I had added this code:
protected override void OnRender(DrawingContext drawingContext)
{
base.OnRender(drawingContext);Star[] stars = new Star[] { One, Two, Three, Four, Five };
for (int i = 0; i < stars.Length; i++)
{
stars[i].Height = this.Height;
stars[i].Width = this.Width;
stars[i].Fill = this.FillBrush;
stars[i].Edge = this.Edge;
stars[i].Filled = Achieved > i;
}
}
I'm assuming this is a bad thing.
To make matters worse, the Filled setter was doing a whole slew of other things.
Anyways, to finish this out, it's still looking a bit funky, but at least it's not blowing up anymore.
Thanks for your tip about the event handlers. It seems like I had a latent stack overflow, but never got there because of the sheer number of things I was requiring the UI to do.
David Morton - http://blog.davemorton.net/ - @davidmmorton- Marked As Answer byDavid M MortonMVPThursday, June 11, 2009 11:09 PM
- Glad I could help.


