.NET垃圾收集行为(使用DataTable obj)

我想知道为什么在创建一个非常简单的DataTable然后将其设置为null后,垃圾收集不会清除该DataTable使用的所有内存。 这是一个例子。 变量Before应该等于Removed但它不是。

{ long Before = 0, After = 0, Removed = 0, Collected = 0; Before = GC.GetTotalMemory(true); DataTable dt = GetSomeDataTableFromSql(); After = GC.GetTotalMemory(true); dt = null; Removed = GC.GetTotalMemory(true); GC.Collect(); Collected = GC.GetTotalMemory(true); } 

给出以下结果。

 Before = 388116 After = 731248 Removed = 530176 Collected = 530176 

几个原因:

GC运行在自己的甜蜜时间; 通常在运行时内存不足时。 这就是处理像DB连接这样的对象很重要的原因; 是的,他们最终会被释放,但直到GC退出运行。

GC.Collect()不直接运行GC线程; 它安排了一系列的GC。 同样,运行时通常只在它注意到沙箱变得混乱或者存在大量空闲时间时才运行GC。 GC.Collect()是一个覆盖,其行为与发生其中一个自动触发器的行为相同。 它不是内联调用来运行垃圾收集算法; 这将导致明显的性能下降。

GC在自己的线程中运行。 因此,GC静态方法提供的信息基于调用者在调用时可用的内容。 您在GC仍在工作时,或者甚至在它开始之前最后一次调用GetTotalMemory,因此内存数字尚未更新GC正在最终确定的内容。

总之,GC的设计主要是放手。 GC.Collect()相当于在酒店门口悬挂“请服务”标志; 这是一个建议,现在可能是清理的好时机。

GC.Collect(); 仅仅是向垃圾收集器建议可能存在需要清理的对象。 GC按照自己的计划运行,很少需要GC.Collect(); 提示。

通过调用GC.Collect();看到对内存的影响的机会GC.Collect(); 在释放资源后立即(微秒)很渺茫。

另外:DataTable对象在GC眼中并不特别。 GC中的任何引用类型都将以相同的方式处理。

GC.GetTotalMemory的文档说明:

垃圾收集器不保证收集所有无法访问的内存。

它表明它只会阻止一小段时间等待垃圾收集和终结者完成。 这个SO答案解释了DataTables没有任何托管资源并且禁止最终化,因此您不需要调用GC.WaitForPendingFinalizers来回收内存。

另一种可能性是,当GC.Collect时, dt不符合收集条件 – 如果有一个类成员或父数据集持有对它的引用,则无法收集它。

此外,与其他一些答案相反, GC.Collect确实强制立即收集(而不仅仅是’提示’) – 文档说明:

强制所有世代立即进行垃圾收集。

本文还说明了这种情况 – 在“垃圾收集条件”部分中,三种可能性之一是:

调用GC.Collect方法。 几乎在所有情况下,您都不必调用此方法,因为垃圾收集器会持续运行。 此方法主要用于独特的情况和测试。

根据http://msdn.microsoft.com/en-us/library/xe0c2357.aspx :

使用此方法尝试回收所有无法访问的内存。

所有对象,无论它们在记忆中存在多长时间,都被考虑收集; 但是,不会收集托管代码中引用的对象。 使用此方法强制系统尝试回收最大可用内存量。

因此,调用Collect()可能不一定会产生你期望它立即产生的东西。

关于.NET上的垃圾收集的文档总是声明它不能保证何时收集。

http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

http://msdn.microsoft.com/en-us/library/ee787088.aspx

http://www.simple-talk.com/dotnet/.net-framework/understanding-garbage-collection-in-.net/ – 这是一篇很好的文章,用于解释垃圾收集和漂亮的图表,使其更容易掌握。

摘自上一篇与您的问题相关的文章:

如果一个对象有一个终结器,当垃圾收集器决定它不再“活动”时,它不会被立即删除。 相反,它成为一种特殊的根,直到.NET调用终结器方法。 这意味着这些对象通常需要从内存中删除多个垃圾收集,因为它们将在第一次被发现未使用时存活。

除了之前关于垃圾收集如何工作的答案之外,DataTable类的第一次访问分配了一些静态变量,当没有DataTable实例存在时,这些变量不会被释放。 这也适用于其他类,具体取决于它们的实现。 创建一个DataTable和所有

由于您正在访问SQL,根据您的GetSomeDataTableFromSql()方法,您可能有一些缓存的SQL连接实例和您不直接了解的其他对象。

因此,分配一个DataTable实例然后摆脱它将不会让你回到你所拥有的相同级别的内存分配。