c# – 即使没有根据,为什么不用终结器收集对象?
如果没有显式调用Dispose(),我遇到了可以通过GC收集的终结对象的问题.
我知道如果一个对象实现了IDisposable,我应该显式调用Dispose(),但我一直认为依赖框架是安全的,当一个对象变为未引用时,它可以被收集. 但在使用windbg / sos / sosex进行一些实验后,我发现如果没有为可终结对象调用GC.SuppressFinalize(),它就不会被收集,即使它没有被引导.因此,如果您广泛使用可终结对象(DbConnection,FileStream等)并且未明确处理它们,则可能会遇到过高的内存消耗或甚至OutOfMemoryException. 这是一个示例应用程序: public class MemoryTest { private HundredMegabyte hundred; public void Run() { Console.WriteLine("ready to attach"); for (var i = 0; i < 100; i++) { Console.WriteLine("iteration #{0}",i + 1); hundred = new HundredMegabyte(); Console.WriteLine("{0} object was initialized",hundred); Console.ReadKey(); //hundred.Dispose(); hundred = null; } } static void Main() { var test = new MemoryTest(); test.Run(); } } public class HundredMegabyte : IDisposable { private readonly Megabyte[] megabytes = new Megabyte[100]; public HundredMegabyte() { for (var i = 0; i < megabytes.Length; i++) { megabytes[i] = new Megabyte(); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } ~HundredMegabyte() { Dispose(false); } private void Dispose(bool disposing) { } public override string ToString() { return String.Format("{0}MB",megabytes.Length); } } public class Megabyte { private readonly Kilobyte[] kilobytes = new Kilobyte[1024]; public Megabyte() { for (var i = 0; i < kilobytes.Length; i++) { kilobytes[i] = new Kilobyte(); } } } public class Kilobyte { private byte[] bytes = new byte[1024]; } 即使经过10次迭代,您仍然可以发现内存消耗过高(从700MB到1GB),并且随着迭代次数的增加会更高.使用WinDBG附加到进程后,您可以发现所有大对象都是无根,但未收集. 如果你明确地调用SuppressFinalize(),情况就会改变:即使在高压下,内存消耗也会稳定在300-400MB左右,而WinDBG显示没有无根对象,内存是免费的. 所以问题是:它是框架中的错误吗?有合理的解释吗? 更多细节: 每次迭代后,windbg显示: >终结队列为空 解决方法
具有终结器的对象的行为与缺少对象的对象的行为不同.
当GC发生并且尚未调用SuppressFinalize时,GC将无法收集实例,因为它必须执行Finalizer.因此,执行终结器,并且对象实例被提升为第1代(在第一个GC中幸存的对象),即使它已经没有任何生命引用. 第1代(和Gen2)对象被认为是长期存在的,只有在Gen1 GC不足以释放足够的内存时才会考虑进行垃圾收集.我认为在测试期间,Gen1 GC总是足够的. 这种行为对GC性能有影响,因为它否定了通过几个générations(你在gen1中有短持续时间对象)带来的优化. 从本质上讲,拥有一个Finalizer并且无法阻止GC调用它将始终将已经死亡的对象提升为长寿堆,这不是一件好事. 因此,您应该正确处理您的IDisposable对象,如果没有必要,请避免使用终结器(如果需要,可以实现IDisposable并调用GC.SuppressFinalize.) 编辑: 将短期对象放入LOH更糟糕,因为它们不会被压缩…因此,如果CLR无法找到足够长的空内存段,则可以使用大量可用内存运行OutOfMemory一大块数据. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |