Ask a questionAsk a question
 

AnswerFlowDocument Never Completes Loading

  • Thursday, June 11, 2009 6:35 PMDavid M MortonMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Has Code
    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

  • Thursday, June 11, 2009 9:58 PMHrach MSFTMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    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.
  • Thursday, June 11, 2009 10:23 PMDavid M MortonMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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

All Replies

  • Thursday, June 11, 2009 8:05 PMHrach MSFTMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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).
  • Thursday, June 11, 2009 8:13 PMDavid M MortonMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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
  • Thursday, June 11, 2009 9:58 PMHrach MSFTMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
    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.
  • Thursday, June 11, 2009 10:23 PMDavid M MortonMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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
  • Thursday, June 11, 2009 11:30 PMHrach MSFTMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Glad I could help.