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.
when press button1
:
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.
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.
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 codeif 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 textbox
es dispose of attached texteditor
s. 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
Post a Comment