我想知道为什么在创建一个非常简单的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
答案 0 :(得分:5)
有几个原因:
GC在自己的甜蜜时光中运行;通常当运行时内存不足时。这就是为什么像DB连接那样处理对象很重要;是的,他们最终会被释放,但直到GC退出运行。GC.Collect()不直接运行GC线程;它安排了一系列的GC。同样,运行时通常只在它注意到沙箱变得混乱或者有大量空闲时间时才运行GC。 GC.Collect()是一个覆盖,其行为与发生其中一个自动触发器的行为相同。它不是内联调用来运行垃圾收集算法;这将导致明显的性能下降。
GC在自己的线程中运行。因此,GC静态方法提供的信息基于调用者在调用时可用的内容。您在GC仍在工作时,或者甚至在它开始之前最后一次调用GetTotalMemory,因此内存数字尚未更新GC正在最终确定的内容。
总之,GC的设计主要是放手。 GC.Collect()相当于挂在酒店门口的“请服务”标志;这是一个建议,现在可能是清理的好时机。
答案 1 :(得分:4)
GC.Collect();
仅仅是对垃圾收集器的建议,可能存在需要清理的对象。 GC按照自己的计划运行,很少需要GC.Collect();
提示。
在您释放资源后立即(微秒)调用GC.Collect();
会对内存产生影响的可能性很小。
另外:DataTable对象在GC眼中并不特别。 GC中的任何引用类型都将以相同的方式处理。
答案 2 :(得分:2)
GC.GetTotalMemory
州的documentation:
垃圾收集器没有 保证所有无法访问的内存 收集。
它表明它只会阻止一小段时间等待垃圾收集和终结者完成。 this SO answer解释说DataTables不包含任何托管资源并禁止最终化,因此您不需要调用GC.WaitForPendingFinalizers
来回收内存。
另一种可能性是dt
在调用GC.Collect
时不符合收集条件 - 如果有一个类成员或父DataSet
持有对它的引用,那么它不可能是收集。
此外,与其他一些答案相反,GC.Collect
会强制立即收集(不只是'提示') - documentation州:
强制立即进行垃圾收集 各代人。
This article也说是这种情况 - 在“垃圾收集条件”部分中,三种可能性之一是:
调用GC.Collect方法。在 几乎所有情况下,你都没有 调用这个方法,因为垃圾 收集器连续运行。这个 方法主要用于唯一 情况和测试。
答案 3 :(得分:1)
关于.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/ - 这是一篇很好的文章,用于解释垃圾收集和漂亮的图表,使其更容易掌握。
摘自上一篇与您的问题相关的文章:
如果某个对象有终结器,那就是 当没有立即删除 垃圾收集器决定它不是 更长的'活'。相反,它变成了一个 特殊的root,直到.NET 称为终结者方法。这个 通常意味着这些对象 需要一个以上的垃圾 要从内存中删除的集合, 因为他们将在第一时间存活下来 发现它们未被使用。
答案 4 :(得分:1)
根据http://msdn.microsoft.com/en-us/library/xe0c2357.aspx:
使用此方法尝试回收所有 无法访问的内存。
所有对象,无论多长时间 他们一直在记忆中 考虑收集;然而, 托管中引用的对象 代码未收集。用这个 迫使系统尝试的方法 收回最高金额 可用内存。
因此,调用Collect()可能不一定会产生你期望它立即产生的东西。
答案 5 :(得分:0)
除了前面关于垃圾收集如何工作的答案之外,DataTable类的第一次访问分配了一些静态变量,当没有DataTable实例存在时,这些变量不会被释放。这也适用于其他类,具体取决于它们的实现。创建DataTable和所有
由于您正在访问SQL,根据您的GetSomeDataTableFromSql()方法,您可能有一些缓存的SQL连接实例和您不直接了解的其他对象。
因此,分配一个DataTable实例然后摆脱它将不会让你回到你所拥有的相同内存分配水平。