mdn*_*mdn 7 .net c# garbage-collection
我有一个简单的类MyDataClass,其成员(obj)实现了IDisposable:
public class MyDataClass : IDisposable
{
private DisposableObject obj;
private List<string> list;
private int c;
public MyDataClass()
{
obj = new DisposableObject();
list = new List<string>();
c = 114;
}
public void Dispose()
{
obj.Dispose();
}
}
public class DisposableObject : IDisposable
{
public void Dispose()
{
// Free resource
Console.WriteLine("Dispose DisposableObject");
}
}
Run Code Online (Sandbox Code Playgroud)
当我运行代码分析时,我得到了CA1063警告,它告诉我应该在MyDataClass实现中的Dispose()方法中调用GC.SuppressFinalize()方法.
我对这个CA1063警告感到很困惑.因为据我所知,我应该调用GC.SuppressFinalize()来指示垃圾收集器:
"嘿GC,不要担心这个对象,因为我已经为你完成了所有的清洁工作!"
所以请确认我是否错了.如果我将添加GC.SuppressFinalize()我将摆脱CA1063但它将导致GC不会清理我的对象.所以我会有内存泄漏,因为其他类成员(托管代码)将不会被清除.
该方法GC.SuppressFinalize()指示VM不运行终结器.这是C#中看起来很滑稽的方法:
~MyDataClass()
要删除警告,您需要密封您的课程,或实施完整的IDisposable模式.
我有一种感觉,尽管您已经接受了答案,但您仍然感到困惑。让我解释:
垃圾收集器 (GC) 有一项令人讨厌的任务,即从内存中删除任何无法访问的对象。
A仅当存在从任何 GC根到对象的任何引用链时,对象才可访问。根的示例是堆栈和任何静态字段。因此,要确定是否A可达,GC 所要做的就是在堆栈上找到一个引用,该引用指向一个对象,该对象具有引用一个对象的引用...引用 object A。如果找不到这样的链,则对象A无法访问。1
因此,一旦 GC 确定A无法访问,它将希望将其从内存中删除。但是,在此之前,GC 会检查是否A有~A必须运行的终结器 ( )。如果没有,它会A从内存中移除并且 GC 完成。
但是,如果A有一个必须运行的终结器,它不能在终结器完成之前从内存中删除对象。所以,它增加了一个参考A到终结队列,并且不从内存中删除对象(还)。现在垃圾收集器已经完成了A。但是,当 GC 再次运行时,它会再次尝试确定是否A可达。幸运的是,终结器队列也是垃圾收集器的根之一,因此它确定终结器队列中存在对 的引用A,因此A是可达的,并且不会再次从内存中删除。
随之而来的是终结器线程,该线程会定期检查终结器队列中是否有任何对象。如果有,它会选择一个并运行它的终结器方法。最终,终结器线程将运行A. 完成此操作后,A将从终结器队列中删除对的引用。
然后,一段时间后,垃圾收集器再次运行,并再次尝试确定是否A可达。由于它现在没有在任何地方被引用,甚至没有从终结器队列中引用,A因此无法访问。GCA从内存中删除。
你看,通常 GC 可以在它检测到的同一个收集周期中删除无法访问的对象,但是当一个对象有一个需要运行的终结器时,它可能需要多个周期才能收集该对象。因此CA1063建议你把GC.SuppressFinalize()在Dispose方法让GC知道,不需要对象最终确定它是从内存中删除了。因此,该对象最终总是从内存中删除2 个。
请注意,当您没有终结器时,您不必添加GC.SuppressFinalize(),因此在这方面 CA1063 警告有点多余。
可以在这篇 MSDN 文章 中找到有关垃圾收集器的更深入的信息。
1)可达性是为什么null在处理它们之后设置引用是很常见的原因。这使得被引用的对象很可能无法访问,因此成为要删除的候选对象。
2)可以(但肯定不推荐)通过使用终结器从另一个可访问对象或根(例如静态字段)添加引用来复活 。这使得再次可达,并且垃圾收集器不会删除它。但是,它的终结器不会再次运行,因为它已经被调用过一次。AAA
如果我添加,
GC.SuppressFinalize()我将摆脱 CA1063,但这会导致 GC 不会清理我的对象。
不,您的物品仍会被收集。
“嘿GC,不用担心这个物体,因为我已经为你完成了所有的清理工作!”
你实际上只是说:不用担心这个对象的终结器(析构函数)。如果有的话。
这就是代码分析出错的地方:你的类确实有一个IDisposable.Dispose()方法,但它没有析构函数。因此,警告是毫无意义的,因为过度保护并且触发错误的原因。禁用或忽略它。