以下代码段说明了打开XPS文件时的内存泄漏。如果您运行它并观察任务管理器,它将会增长,并且在应用程序退出之前不会释放内存。
'******控制台应用程序BEGINS。
Module Main
Const DefaultTestFilePath As String = "D:\Test.xps"
Const DefaultLoopRuns As Integer = 1000
Public Sub Main(ByVal Args As String())
Dim PathToTestXps As String = DefaultTestFilePath
Dim NumberOfLoops As Integer = DefaultLoopRuns
If (Args.Count >= 1) Then PathToTestXps = Args(0)
If (Args.Count >= 2) Then NumberOfLoops = CInt(Args(1))
Console.Clear()
Console.WriteLine("Start - {0}", GC.GetTotalMemory(True))
For LoopCount As Integer = 1 To NumberOfLoops
Console.CursorLeft = 0
Console.Write("Loop {0:d5}", LoopCount)
' The more complex the XPS document and the more loops, the more memory is lost.
Using XPSItem As New Windows.Xps.Packaging.XpsDocument(PathToTestXps, System.IO.FileAccess.Read)
Dim FixedDocSequence As Windows.Documents.FixedDocumentSequence
' This line leaks a chunk of memory each time, when commented out it does not.
FixedDocSequence = XPSItem.GetFixedDocumentSequence
End Using
Next
Console.WriteLine()
GC.Collect() ' This line has no effect, I think the memory that has leaked is unmanaged (C++ XPS internals).
Console.WriteLine("Complete - {0}", GC.GetTotalMemory(True))
Console.WriteLine("Loop complete but memory not released, will release when app exits (press a key to exit).")
Console.ReadKey()
End Sub
End Module
'******控制台应用程序ENDS。
它循环一千次的原因是因为我的代码处理了大量文件并快速泄漏内存,导致OutOfMemoryException。强制垃圾收集不起作用(我怀疑它是XPS内部的非托管内存块)。
代码最初是在另一个线程和类中,但已经简化为此。
非常感谢任何帮助。
赖安
答案 0 :(得分:7)
好吧,我找到了。这是框架中的一个错误,为了解决这个问题,您可以添加对UpdateLayout的调用。可以将使用语句更改为以下内容以提供修复;
Using XPSItem As New Windows.Xps.Packaging.XpsDocument(PathToTestXps, System.IO.FileAccess.Read)
Dim FixedDocSequence As Windows.Documents.FixedDocumentSequence
Dim DocPager As Windows.Documents.DocumentPaginator
FixedDocSequence = XPSItem.GetFixedDocumentSequence
DocPager = FixedDocSequence.DocumentPaginator
DocPager.ComputePageCount()
' This is the fix, each page must be laid out otherwise resources are never released.'
For PageIndex As Integer = 0 To DocPager.PageCount - 1
DirectCast(DocPager.GetPage(PageIndex).Visual, Windows.Documents.FixedPage).UpdateLayout()
Next
FixedDocSequence = Nothing
End Using
答案 1 :(得分:5)
今天进入这个。有趣的是,当我使用Reflector.NET注意事物时,我发现修复涉及在与当前Dispatcher关联的ContextLayoutManager上调用UpdateLayout()。 (阅读:无需迭代页面)。
基本上,要调用的代码(在此使用反射)是:
ContextLayoutManager.From(Dispatcher.CurrentDispatcher).UpdateLayout();
绝对感觉像是MS的一个小疏忽。
对于懒惰或不熟悉,此代码有效:
Assembly presentationCoreAssembly = Assembly.GetAssembly(typeof (System.Windows.UIElement));
Type contextLayoutManagerType = presentationCoreAssembly.GetType("System.Windows.ContextLayoutManager");
object contextLayoutManager = contextLayoutManagerType.InvokeMember("From",
BindingFlags.InvokeMethod | BindingFlags.Static | BindingFlags.NonPublic, null, null, new[] {dispatcher});
contextLayoutManagerType.InvokeMember("UpdateLayout", BindingFlags.InvokeMethod | BindingFlags.NonPublic | BindingFlags.Instance, null, contextLayoutManager, null);
FxCop会抱怨,但也许它已在下一个框架版本中得到修复。如果您不想使用反射,作者发布的代码似乎“更安全”。
HTH!
答案 2 :(得分:0)
我不能给你任何权威的建议,但我确实有一些想法:
答案 3 :(得分:0)
添加UpdateLayout无法解决问题。 根据{{3}},“需要在主应用程序域中预加载PresentationCore.dll文件或PresentationFramework.dll文件”。
答案 4 :(得分:0)
有趣。问题仍然存在于.net框架4.0中。我的代码凶猛地泄漏了。
建议的修复 - 在创建FixedDocumentSequence后立即在循环中调用UpdateLayout并没有在400页测试文档中解决我的问题。
但是,以下解决方案DID为我解决了这个问题。与之前的修复一样,我将调用移动到for-each-pages循环之外的GetFixedDocumentSequence()。 “使用”条款......公平警告,我仍然不确定它是否正确。但它并没有伤害。随后,该文档将重新用于在屏幕上生成页面预览。所以它似乎没有受伤。
DocumentPaginator paginator
= document.GetFixedDocumentSequence().DocumentPaginator;
int numberOfPages = paginator.ComputePageCount();
for (int i = 0; i < NumberOfPages; ++i)
{
DocumentPage docPage = paginator.GetPage(nPage);
using (docPage) // using is *probably* correct.
{
// VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV
((FixedPage)(docPage.Visual)).UpdateLayout();
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// Adding THAT line cured my leak.
RenderTargetBitmap bitmap = GetXpsPageAsBitmap(docPage, dpi);
.... etc...
}
}
实际上,修复行会进入我的GetXpsPageAsBitmap例程(为清晰起见而省略),这与之前发布的代码完全相同。
感谢所有贡献者。