我有一个简单的类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");
}
}
当我运行代码分析时,我收到CA1063警告,告诉我应该在 Dispose()方法中调用 GC.SuppressFinalize()方法> MyDataClass 实施。
我对此CA1063警告感到困惑。因为据我所知,我应该调用 GC.SuppressFinalize()来指示垃圾收集器:
“嘿GC,不要担心这个对象,因为我已经完成了所有的清洁工作!”
所以请确认我是否错了。如果我要添加 GC.SuppressFinalize(),我将摆脱CA1063,但这将导致GC不会清理我的对象。所以我会有内存泄漏,因为其他类成员(托管代码)将不会被清除。
答案 0 :(得分:5)
方法GC.SuppressFinalize()
表示VM不运行终结器。这是C#中看起来很滑稽的方法:
~MyDataClass()
要删除警告,您需要密封课程,或实施完整的IDisposable pattern。
答案 1 :(得分:4)
垃圾收集器(GC)具有从内存中删除无法访问的任何对象的不可靠任务。
只有从任何GC root 到对象的任何引用链时,才能访问对象A
。根的示例是堆栈和任何静态字段。因此,要确定是否可以访问A
,所有GC必须做的是在堆栈上找到引用具有引用引用对象的引用的引用...引用对象{{ 1}}。如果找不到这样的链,则无法访问对象A
。 1
因此,一旦GC确定A
无法访问,它就会想要从内存中删除它。
但是,在执行此操作之前,GC会检查A
是否具有必须运行的终结器(A
)。如果没有,它会从内存中删除~A
并使用它完成GC。
但是,如果A
具有必须运行的终结器,则在终结器完成之前,它无法从内存中删除该对象。因此,它将A
的引用添加到终结器队列,并且不会从内存中删除该对象(尚未)。现在,垃圾收集器已完成A
。但是,当GC再次运行时,它将再次尝试确定是否可以访问A
。幸运的是,终结器队列也是垃圾收集器的根之一,因此它确定终结器队列中有一个引用到A
,因此A
可以访问,并且不会再从内存中删除。
沿着终结器线程,这个线程定期检查终结器队列中是否有任何对象。如果有,它选择一个并运行其终结器方法。最终,终结器线程将运行A
的终结器。完成此操作后,将从终结器队列中删除对A
的引用。
然后,一段时间后,垃圾收集器再次运行,并尝试再次确定是否可以访问A
。由于现在没有在任何地方引用,甚至在终结器队列中都没有引用,因此无法访问A
。 GC从内存中删除A
。
您可以看到,GC通常会在检测到它们的同一个收集周期中删除无法访问的对象,但是当一个对象有一个需要运行的终结器时,可能需要多个周期才能收集该对象。因此,CA1063建议您将A
放在GC.SuppressFinalize()
方法中,让GC知道在从内存中删除对象之前不需要最终确定对象。因此,对象总是从内存中删除 2 。
请注意,当您没有终结器时,您不必添加Dispose
,因此在这方面,CA1063警告有点多余。
可以找到有关垃圾收集器的更深入信息in this MSDN article。
1 )可达性是在处理它们之后设置对GC.SuppressFinalize()
的引用的常见原因。这使得引用的对象很可能无法访问,因此被删除的候选对象。
2 ) 复活 null
可能(但肯定不推荐)使用终结器从另一个可到达的对象或根(例如静态字段)添加对A
的引用。这使A
再次可以访问,垃圾收集器不会删除它。但是,它的终结器不会再次运行,因为它已被调用过一次。
答案 2 :(得分:2)
如果我要添加
GC.SuppressFinalize()
,我将摆脱CA1063 但这会导致GC无法清理我的物体。
不,您的对象仍将被收集。
“嘿GC,不要担心这个对象,因为我已经完成了所有的清洁工作!”
你实际上只是说:不要担心这个对象的Finalizer(析构函数)。如果有的话。
这就是代码分析错误的地方:你的类确实有一个IDisposable.Dispose()
方法,但它没有析构函数。因此,警告毫无意义,过度保护并触发错误的原因。禁用或忽略它。