c# - ElementHost + FlowDocument = GC not working, memory keeps increasing -



c# - ElementHost + FlowDocument = GC not working, memory keeps increasing -

[updated, see bottom!]

there memory leak in our winforms application hosting wpf flowdocumentreader in elementhost. have recreated issue in simple project , added code below.

what application does

when press button1:

a usercontrol1 contains flowdocumentreader created , set elementhost's child a flowdocument created text file (it contains flowdocument stackpanel few one thousand lines of <textbox/>) the flowdocumentreader's document property set flowdocument

at point, page renders flowdocument correctly. lot of memory used, expected.

the problem

if button1 clicked again, memory usage increases, , keeps increasing every time process repeats! gc isn't collecting despite loads of new memory beingness used! there no references shouldn't there, because:

if press button2 sets elementhost1.child null , invokes gc (see code below), weird thing happens - not clean memory, if maintain clicking few seconds, free it!

it unacceptable memory stays used. also, removing elementhost controls collection, disposing it, setting reference null, , invoking gc not free memory.

what want if button1 clicked mutiple times, memory usage shouldn't maintain going up i should able free memory (this 1 window in "real" application, , want when closed)

this not memory usage doesn't matter , can allow gc collect whenever. ends noticeably slowing downwards machine.

the code

if rather download vs project, i've uploaded here: http://speedy.sh/8t5p2/windowsformsapplication7.zip

otherwise, here relevant code. add together 2 buttons form in designer , hook them events. form1.cs:

using system; using system.collections.generic; using system.componentmodel; using system.data; using system.drawing; using system.linq; using system.text; using system.windows.forms; using system.windows.documents; using system.io; using system.xml; using system.windows.markup; using system.windows.forms.integration; namespace windowsformsapplication7 { public partial class form1 : form { private elementhost elementhost; public form1() { initializecomponent(); } private void button1_click(object sender, eventargs e) { string rawxamltext = file.readalltext("in.txt"); using (var flowdocumentstringreader = new stringreader(rawxamltext)) using (var flowdocumenttextreader = new xmltextreader(flowdocumentstringreader)) { if (elementhost != null) { controls.remove(elementhost); elementhost.child = null; elementhost.dispose(); } var uc1 = new usercontrol1(); object document = xamlreader.load(flowdocumenttextreader); var fd = document flowdocument; uc1.docreader.document = fd; elementhost = new elementhost(); elementhost.dock = dockstyle.fill; elementhost.anchor = anchorstyles.top | anchorstyles.bottom | anchorstyles.left | anchorstyles.right; controls.add(elementhost); elementhost.child = uc1; } } private void button2_click(object sender, eventargs e) { if (elementhost != null) elementhost.child = null; gc.collect(); gc.waitforpendingfinalizers(); gc.collect(); } } }

usercontrol1.xaml

<usercontrol x:class="windowsformsapplication7.usercontrol1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:ignorable="d" d:designheight="300" d:designwidth="300"> <flowdocumentreader x:name="docreader"></flowdocumentreader> </usercontrol> edit:

i have time deal again. tried instead of reusing elementhost, disposing , recreating each time button pressed. while help bit, in sense memory going , downwards when spam click button1 instead of going up, still doesn't solve problem - memory going overall , not freed when form closed. putting bounty up.

as there seems have been confusion wrong here, here exact steps reproduce leak:

1) open task manager

2) click "start" button open form

3) spam dozen or 2 clicks on "go" button , watch memory usage - now should notice leak

4a) close form - the memory won't released.

or

4b) spam "clean" button few times, the memory released, indicating not reference leak, gc/finalization problem

what need prevent leak @ step 3) , free memory @ step 4a). "clean" button isn't there in actual application, here show there no hidden references.

i used clr profiler check memory profile after hitting "go" button few times (memory usage ~350 mb @ point). turns out, there 16125 (5x amount in document) controls.textbox , 16125 controls.textboxview both rooted in 16125 documents.texteditor objects rooted in finalization queue - see here:

http://i.imgur.com/m28aiux.png

any insight appreciated.

another update - solved (kind of)

i ran 1 time again in different, pure wpf application not utilize elementhost or flowdocument, in retrospect, title misleading. explained anton tykhyy, bug wpf textbox itself, not correctly dispose of texteditor.

i did not workarounds anton suggested, explanation of bug useful rather ugly, short solution.

when destroy instance of command contains textboxes, (in code-behind of control):

var textboxes = findvisualchildren<textbox>(this).tolist(); foreach (var textbox in textboxes) { var type = textbox.gettype(); object texteditor = textbox.gettype().getproperty("texteditor", bindingflags.nonpublic | bindingflags.instance).getvalue(textbox, null); var ondetach = texteditor.gettype().getmethod("ondetach", bindingflags.nonpublic | bindingflags.instance); ondetach.invoke(texteditor, null); }

where findvisualchildren is:

public static ienumerable<t> findvisualchildren<t>(dependencyobject depobj) t : dependencyobject { if (depobj != null) { (int = 0; < visualtreehelper.getchildrencount(depobj); i++) { dependencyobject kid = visualtreehelper.getchild(depobj, i); if (child != null && kid t) { yield homecoming (t)child; } foreach (t childofchild in findvisualchildren<t>(child)) { yield homecoming childofchild; } } } }

basically, textbox should doing. in end phone call gc.collect() (not strictly necessary helps free memory faster). ugly solution seems solve problem. no more texteditors stuck in finalization queue.

indeed, presentationframework.dll!system.windows.documents.texteditor has finalizer , gets stuck on finalizer queue (together stuff hanging off it) unless disposed of properly. scrounged around in presentationframework.dll bit, , unfortunately don't see how textboxes dispose of attached texteditors. relevant phone call textbox.ondetach in textboxbase.initializetextcontainer(). there can see 1 time textbox creates texteditor, disposes of in exchange creating new one. 2 other conditions under texteditor disposes when application domain unloads or wpf dispatcher shuts down. former looks more hopeful, found no way restart shut-down wpf dispatcher. wpf objects cannot straight shared across application domains because not derive marshalbyrefobject, windows forms controls do. seek putting elementhost in separate application domain , tear downwards when clearing form (you may need shut downwards dispatcher first). approach utilize maf add-ins set wpf command different application domain; see this question.

c# wpf winforms flowdocument elementhost

Comments

Popular posts from this blog

web services - java.lang.NoClassDefFoundError: Could not initialize class net.sf.cglib.proxy.Enhancer -

Accessing MATLAB's unicode strings from C -

javascript - mongodb won't find my schema method in nested container -