8

我目前正在尝试处理释放 FlowDocument 资源的问题。我正在加载一个 rtf 文件并将其放入带有 TextRange.Load 的 FlowDocument 中。我注意到,在它这样做之后,它会保留这些资源,而 GC 不会收集它。我运行了一个内存分析器,并且发现这是真的。我还将范围缩小到我是否加载实际将 rtf 放入 FlowDocument 中。如果我不这样做,那么一切都很好。所以我知道这是问题所在。

我希望对如何解决这个问题有一些指导。这是加载 rtf 和所有内容的代码。我已将所有其他代码注释掉,甚至将其放在自己的范围内,并尝试了 GC.Collect()。任何帮助是极大的赞赏。

编辑:这是我目前的完整代码。除了让它运行的基本必需品外,我已经取出了其他所有东西。问题仍然存在。如您所见,FlowDocument 和 TextRange 没有在其他任何地方引用。

    public LoadRTFWindow(string file)
    {
        InitializeComponent();

        using (FileStream reader = new FileStream(file, FileMode.Open))
        {
            FlowDocument doc = new FlowDocument();
            TextRange range = new TextRange(doc.ContentStart, doc.ContentEnd);
            range.Load(reader, System.Windows.DataFormats.Rtf);
        }
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
    }

我找到了这篇文章,我希望它可以帮助我解决我的问题,但我没有运气。非常感谢任何类型的帮助。谢谢你。

编辑:我想我应该提到我检查这个的主要方式。我打开了 Windows 任务管理器,正在查看我的应用程序进程正在使用的内存使用情况。当我运行上面的代码时,应用程序在执行 TextRange.Load()(这是一个 400 页的大 RTF)时从 40,000K 变为 70,000K,一旦完成,它就会下降到 61,000K 并保持在那里。我的期望是它会回落到 40,000K 或至少非常接近它。

正如我之前提到的,我使用了一个内存分析器,发现有很多段落,Run..ect。之后对象仍然活着。

4

8 回答 8

6

如果我已经确认存在内存泄漏,这就是我要调试问题的方法。

  1. 从http://www.microsoft.com/whdc/devtools/debugging/installx86.mspx#a安装适用于 Windows 的调试工具
  2. 从安装目录启动 Windbg。
  3. 启动您的应用程序并执行泄漏内存的操作。
  4. 将 Windbg 附加到您的应用程序 (F6)。
  5. 类型.loadby sos mscorwks
  6. 类型!dumpheap -type FlowDocument
  7. 检查上述命令的结果。如果您看到多个 FlowDocument,对于第一列(包含地址)的每个值,请执行

类型!gcroot <value of first column>

这应该告诉你谁在坚持参考。

于 2009-06-23T04:26:01.547 回答
3

我们有一个类似的问题,我们在不同的线程中创建流文档,我在内存分析器中注意到对象仍然存在。

据我所知,如本链接所述

“创建 FlowDocument 时,还会在其 StructuralCache 中为其创建相对昂贵的格式化上下文对象。当您在紧密循环中创建多个 FlowDocs 时,会为每个 FlowDoc 创建一个 StructuralCache。让我们在末尾调用 Gc.Collect循环,希望恢复一些内存。StructuralCache 有一个终结器释放此格式化上下文,但不是立即释放。终结器有效地调度操作以在 DispatcherPriority.Background 释放上下文。

因此,在调度程序操作完成之前,Flow 文档将在内存中。所以思路是完成dispatcher的操作。

如果您处于 Dispatcher 当前正在运行的线程中,请尝试以下代码,它将强制完成队列中的所有操作,因为 SystemIdle 是最低优先级:

Dispatcher.CurrentDispatcher.Invoke(DispatcherPriority.SystemIdle, 
    new DispatcherOperationCallback(delegate { return null; }), null); 

如果您在 Dispatcher 未运行的线程中,例如在我的情况下,在线程中只创建了单个流文档,所以我尝试了类似的方法:

var dispatcher = Dispatcher.CurrentDispatcher;
dispatcher.BeginInvokeShutdown(DispatcherPriority.SystemIdle);
Dispatcher.Run();

这将在最后排队关闭,然后运行调度程序以清理 FlowDocument,然后最终关闭调度程序。

于 2010-11-12T15:34:44.483 回答
1

FlowDocument 使用 System.Windows.Threading.Dispatcher 释放所有资源。它不使用终结器,因为终结器将阻塞当前线程,直到所有资源都空闲为止。所以用户可能会看到一些 UI 冻结等等。调度程序在后台线程中运行,对 UI 的影响较小。
所以调用 GC.WaitForPendingFinalizers(); 对此没有影响。您只需要添加一些代码来等待并让 Dispatchers 完成他们的工作。只需尝试添加类似 Thread.CurrentThread.Sleep(2000 /* 2 secs */);

编辑:在我看来,您在调试某些应用程序时发现了这个问题。我编写了以下非常简单的测试用例(控制台程序):

    static void Main(string[] args)
    {
        Console.WriteLine("press enter to start");
        Console.ReadLine();

        var path = "c:\\1.rtf";

        for (var i = 0; i < 20; i++)
        {
            using (var stream = new FileStream(path, FileMode.Open))
            {
                var document = new FlowDocument();
                var range = new TextRange(document.ContentStart, document.ContentEnd);

                range.Load(stream, DataFormats.Rtf);
            }
        }

        Console.WriteLine("press enter to run GC.");
        Console.ReadLine();

        GC.Collect();
        GC.WaitForPendingFinalizers();

        Console.WriteLine("GC has finished .");
        Console.ReadLine();
    }

我试图重现这个问题。我运行了几次,它运行良好 - 没有泄漏(大约 3,2Kb 的所有时间和 36 个句柄)。我无法重现它,直到我在 VS 中以调试模式运行该程序(只是 f5 而不是 ctrl+f5)。一开始我收到了 20,5Kb,加载后和 GC 之前收到了 31,7Kb,GC 之后收到了 31Kb。它看起来与您的结果相似。
那么,您能否尝试在发布模式下而不是在 VS 下重现此问题?

于 2009-06-05T00:09:15.007 回答
1

我之前完成了#7。这是我第一次使用Windbg,所以我不知道如何处理地址来查找参考。这是我得到的。

 Address       MT     Size
0131c9c0 55cd21d8       84     
013479e0 55cd21d8       84     
044dabe0 55cd21d8       84     
total 3 objects
Statistics:
      MT    Count    TotalSize Class Name
55cd21d8        3          252 System.Windows.Documents.FlowDocument
Total 3 objects
0:011> !gcroot 0131c9c0
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread 47c
Scan Thread 2 OSTHread be8
Scan Thread 4 OSTHread 498
DOMAIN(001657B0):HANDLE(WeakSh):911788:Root:0131ff98(System.EventHandler)->
0131fcd4(System.Windows.Documents.AdornerLayer)->
012fad68(MemoryTesting.Window2)->
0131c9c0(System.Windows.Documents.FlowDocument)
DOMAIN(001657B0):HANDLE(WeakSh):911cb0:Root:0131ca90(MS.Internal.PtsHost.PtsContext)->
0131cb14(MS.Internal.PtsHost.PtsContext+HandleIndex[])->
0133d668(MS.Internal.PtsHost.TextParagraph)->
0131c9c0(System.Windows.Documents.FlowDocument)
DOMAIN(001657B0):HANDLE(WeakSh):9124a8:Root:01320a2c(MS.Internal.PtsHost.FlowDocumentPage)->
0133d5d0(System.Windows.Documents.TextPointer)->
0131ca14(System.Windows.Documents.TextContainer)->
0131c9c0(System.Windows.Documents.FlowDocument)

(我把它放在一个代码块中以便于阅读)。这是在我关上窗户之后。所以看起来它被一些东西引用了。现在我知道了如何释放这些引用,以便它们可以释放 FlowDocument。

谢谢你的帮助。我觉得我终于取得了一些进展。

于 2009-06-24T03:04:01.347 回答
0

GC.Collect()本身不会收集所有内容,您需要运行:

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

此外,我发现运行时并不总是立即释放收集的内存,您应该检查实际堆大小而不是依赖任务管理器。

于 2009-06-14T04:37:29.677 回答
0

考虑释放该文件句柄。还可以考虑使用“using”语句而不是调用 IDisposable.Dispose(没有双关语)。

于 2009-06-14T20:50:08.263 回答
0

我试图重现您的问题,但它没有发生在我的机器上。

任务管理器显示工作集大小,它不能准确表示程序的内存使用情况。尝试改用 perfmon。

  1. 开始 -> 运行 -> perfmon.msc
  2. 为您的应用程序在所有堆中添加 .NET CLR 内存/#Bytes

现在重复实验并检查该计数器是否继续上升。如果没有,则意味着您没有泄漏任何托管内存。

于 2009-06-22T19:14:30.747 回答
0

确保 FlowDocument 的父级没有闲逛,请参见此处。“实例化 FlowDocument 会自动生成承载内容的父 FlowDocumentPageViewer。” 如果该控件悬而未决,则可能是您的问题的根源。

于 2009-06-04T21:01:58.333 回答