以下代码段说明了打开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内部的非托管大块内存).
代码最初是在另一个线程和类中,但已经简化为此.
任何帮助非常感谢.
瑞安
好吧,我找到了.这是框架中的一个错误,为了解决这个问题,您可以添加对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
今天进入这个.有趣的是,当我使用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!