如何确定是什么/谁阻止了对象被垃圾回收(C#)

3

我已经在开发一个C#/WPF应用程序了一段时间,我认为我的某些对象没有像我预期的那样被垃圾收集。

我是如何做出这个判断的呢?

我在我期望被垃圾收集的类中添加了finalizers,并设置了断点。我运行我的应用程序,触发一系列事件来a)创建对象并b)使对象在理论上被废除。例如,我打开对话框,然后关闭它等。我的finalizers没有被调用。

为了确保GC没有花费过长的时间,或者对象以某种方式晋升到旧一代,因此不能以相同的速度进行收集,我甚至创建了一个线程,定期强制进行垃圾收集,即:

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

仍然没有触发终结器断点。 我相当确定有某些引用阻止这些对象被回收,但我无论如何都无法确定是什么。

所以我的问题有两个方面:

  1. 根据我的测试,我的结果是否有效?
  2. 如果有效,是否有一些机制或工具可以确定对象引用(即对象引用链的视觉表示)?

提前感谢!


1
也许析构函数没有运行是因为内存不需要释放。垃圾回收器决定何时需要释放内存。 - Tim Schmelter
你尝试过在Visual Studio中使用内存分析器吗?我假设它适用于你的项目类型。 - Peter Torr - MSFT
1
内存分析器向我展示了分配和使用情况,但我需要找出谁持有特定对象的引用。我可以看到分析器对于确定性能很有用,但似乎无法获得我正在寻找的详细信息(我不否认我可能缺乏有效使用分析器工具的知识...) - Helstrom
不要试图在调试器中验证这个 - 它可能会延长对象的生命周期。运行一个发布版本并使用日志记录。 - H H
发布相关代码(大纲)。到目前为止,您还没有证明这里存在任何问题。 - H H
2个回答

2
这可能不是你问题的确切答案,因为我看不到你的代码 - 但我通常发现,当一个对象没有被垃圾收集时,这通常是因为我忘记在某个地方取消订阅事件。当您订阅事件时,您创建了一个引用,这将防止您的类被回收。

另外,垃圾收集器是一个非常不可预测的过程。我记得在某个地方读到过,即使您调用GC.Collect也不能保证您的未引用类会立即被收集。


这就是我期望的,但是我怎么也找不到任何未取消订阅的事件订阅 :( - Helstrom

0
通过调用GC.CollectGC.WaitForPendingFinalizers,您可以确保finalizer将启动,但您还需要确保此代码正在调用。在WaitForPendingFinalizers之后的方法闭包右侧断点,以确保代码正在执行。
由于这是一个WPF项目,因此在您正在使用的任何视图中具有PreviewKeyDown,通过检查特定键然后在否则无操作的if中断点插入“调试按钮”可能也会有所帮助。在此处放置垃圾收集代码应同样确保您的代码正在执行。
如上所述,事件处理程序也可以是硬引用,因此请注意您正在使用事件的所有位置,并意识到WPF会相当频繁地钩入PropertyChanged

我已经这样做了。所以我可以验证我正在强制进行GC进程,但我没有看到我的终结器被调用。所以我确信有某个地方还存在引用。我猜此时我试图找到的主要问题是如何确定谁正在持有这些引用。 - Helstrom
好的,你的IDE应该允许你搜索任何对象的所有引用。我自己已经有一段时间没有使用Visual Studio了,但我想它应该可以做到这一点。MonoDevelop可以。只需查看对你认为应该被收集但未被收集的任何对象的引用,并查看是否可以确定其中任何一个是阻止收集的原因。基本上,这是一个排除法的过程。 - user4356796

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接