【问题标题】:FlowDocument Memory Issue in C# (But WinDbg gcroot shows “Found 0 unique roots”C# 中的 FlowDocument 内存问题(但 WinDbg gcroot 显示“找到 0 个唯一根”
【发布时间】:2021-11-15 15:38:40
【问题描述】:

您好,我在 wpf 中创建了一个窗口。 这是一个打印预览屏幕。 它在 DocumentViewer 中显示 (FixedDocumentSequence)。 FixedDocumentSequence 是由 Flow Document 使用动态绑定创建的。

单个窗口占用近500MB。 每次内存泄漏大约 100MB。 如果打开超过 5 或 10 次内存溢出 1GB。

我在 STA 线程(不是 UI 线程)中打开窗口。执行 Dispatcher 关闭方法。但是有些对象没有从内存中释放。(调度程序关闭后内存对象仍然存在?)

我已经使用 WinDbg 检查内存泄漏,Flow 文档部分仍然显示。

 !dumpheap -stat

我检查对象的根。但它显示“找到 0 个唯一根”

 !gcroot [address]

我的英语可能不太理解问题的意思。

[问题1]请给我一些解决内存泄漏的建议。

(另外一个信息和问题:

我也想尝试在 dispose 方法中设置 null。

GC 终结器从另一个线程调用析构函数。 所以运行时看不到对象的值,也不能在 dispose 方法中将值设置为 null。

[问题 2] 我已经关闭了调度程序。那么有必要在 Dispose(bool disposing) 方法中实现什么吗?)

【问题讨论】:

  • 如果您的对象没有根,那么它不太可能是内存泄漏。可能只是 GC 还没有回收内存。尝试运行GC.Collect(2) 看看会发生什么
  • GC.Collect(2) 可以帮助一些更好的东西。谢谢。手动 Dispose 一些对象,然后在 ShutDown 之后,Thread sleep 1secod,然后调用 GC.Collect(2)。解决了 90%。

标签: wpf memory-leaks windbg flowdocument


【解决方案1】:

每次内存泄漏大约 100MB。

其实这很好。可重现的内存泄漏应该很容易修复。

我在 STA 线程(不是 UI 线程)中打开窗口。

我不明白。如果使用Application.Run(),该线程将成为 UI 线程。

我检查对象的根。

哪个对象?为什么选择它?

但它显示“找到 0 个唯一根”

这意味着它有资格进行垃圾收集。下次垃圾收集器运行时,它可能会消失。

我也想尝试在 dispose 方法中设置 null。

对于报告为“找到 0 个唯一根”的对象,已经完成。

但即使您将所有变量都设置为null,对象仍然会存在。你只是没有参考了。

GC 终结器从另一个线程调用析构函数。

这很正常。有一个专用的终结器线程。

因此运行时无法看到对象值,也无法在 dispose 方法中将值设置为 null。

您可以在终结器线程内将变量设置为null。所有线程都可以访问内存。

那么有必要在 Dispose(bool disposing) 方法中实现什么吗?)

不知道你的窗口使用什么资源,这很难说。

如何进行?

首先,获得比 WinDbg 更好的工具。可以用WinDbg完成,但要费很大力气。无论您使用什么工具:

  1. 运行泄漏的应用程序
  2. 重现泄漏
  3. 强制进行垃圾回收(如果不能这样做,请实现一个按钮)
  4. 拍摄快照(基本上是完整的!dumpheap
  5. 如果可能,再次泄漏 7 次(否则 5 或 3 次)
  6. 再次强制垃圾回收
  7. 再次拍摄快照
  8. 比较两个快照(创建差异)

在快照差异中,您现在可以查找具有 7 倍数的新对象(例如 21、70、91,...)。这些是泄漏的。检查这些的 GC 根。

当您重现泄漏 7 次时,泄漏 5、6、8 或 9 个对象的可能性很小。我选择素数或至少是奇数,因为计算机程序中自然会出现 2、4 和 8 的倍数。

在 WinDbg 中用!dumpheap 的输出做差异是很难的。您可能需要.logopen 来捕获所有输出。也许你最终会为日志文件编写自己的解析器。

我看到很多开发人员使用 Jetbrains Resharper。一些许可证包括dotMemory。我对此很满意,它完成了工作。

【讨论】:

  • 感谢您为我解决问题的美好时光。收到您的回复后,我得到了鼓励。多谢。 > 我不明白。如果您使用 Application.Run(),该线程将成为 UI 线程。 eprystupa.wordpress.com/2008/07/28/… GC.Collect(2) 帮助更好。谢谢。手动 Dispose 一些对象,然后在 ShutDown 之后,Thread sleep 1secod,然后调用 GC.Collect(2)。解决了 90%。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-18
  • 2018-09-17
  • 2022-08-10
  • 1970-01-01
  • 2014-03-30
  • 2022-12-14
  • 1970-01-01
相关资源
最近更新 更多