简体   繁体   English

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

[英].NET Garbage Collection behavior (with DataTable obj)

I am wondering why after creating a very simple DataTable and then setting it to null does the Garbage Collection not clear out all the memory used by that DataTable. 我想知道为什么在创建一个非常简单的DataTable然后将其设置为null后,垃圾收集不会清除该DataTable使用的所有内存。 Here is an example. 这是一个例子。 The variable Before should be equal to Removed but it is not. 变量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);
}

Gives the following results. 给出以下结果。

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

Several reasons: 几个原因:

GC runs in its own sweet time; GC运行在自己的甜蜜时间; usually when the runtime is short on memory. 通常当运行时内存不足时。 This is why disposing objects like DB connections is important; 这就是为什么像DB连接那样处理对象很重要; yes they'll be released eventually, but not until GC deigns to run. 是的,他们最终会被释放,但直到GC才能运行。

GC.Collect() does not run the GC thread directly; GC.Collect()不直接运行GC线程; it schedues a run of GC. 它安排了一系列的GC。 Again, the runtime normally only runs GC when it notices the sandbox is getting cluttered, or if there is significant idle time. 同样,运行时通常只在它注意到沙箱变得混乱或者有大量空闲时间时才运行GC。 GC.Collect() is an override that behaves the same as if one of these automatic triggers had happened. GC.Collect()是一个覆盖,其行为与发生其中一个自动触发器的行为相同。 it is not an inline call to run the garbage collection algorithm; 它不是内联调用来运行垃圾收集算法; that would result in noticeable performance degradation. 这将导致明显的性能下降。

GC runs in its own thread. GC在自己的线程中运行。 Therefore, information provided by the GC static methods are based on what is available to the caller at the time it's called. 因此,GC静态方法提供的信息基于调用者在调用时可用的内容。 You are calling GetTotalMemory for the last time while the GC is still working, or maybe before it even starts, and so the memory figures haven't been updated with things the GC is finalizing. 您在GC仍在工作时,或者甚至在它开始之前最后一次调用GetTotalMemory,因此内存数字尚未更新GC正在最终确定的内容。

In summary, GC is designed to be largely hands-off. 总之,GC的设计主要是放手。 GC.Collect() is equivalent to hanging the "please service" sign on your hotel door; GC.Collect()相当于挂在酒店门口的“请服务”标志; it's a suggestion that maybe now would be a good time to clean up. 这是一个建议,也许现在是清理的好时机。

GC.Collect(); is merely a suggestion to the Garbage Collector that there may be objects that need to be cleaned up. 仅仅是向垃圾收集器建议可能存在需要清理的对象。 The GC runs on its own schedule and it's very rare that it will need the GC.Collect(); GC按照自己的时间表运行,很少需要GC.Collect(); prompting. 提示。

The chances of seeing an impact on memory by calling GC.Collect(); 通过调用GC.Collect();看到对内存的影响的可能性GC.Collect(); immediately (microseconds) after you've released a resource are slim. 释放资源后立即(微秒)很渺茫。

Also: The DataTable object isn't special in the eyes of the GC. 另外:DataTable对象在GC眼中并不特别。 Any reference type in .NET will be treated by the GC in the same way. GC中的任何引用类型都将以相同的方式处理。

The documentation for GC.GetTotalMemory states: GC.GetTotalMemory文档说明:

The garbage collector does not guarantee that all inaccessible memory is collected. 垃圾收集器不保证收集所有无法访问的内存。

It suggests that it will only block for a short interval to wait for garbage collection and finalisers to complete. 它表明它只会阻止一小段时间等待垃圾收集和终结者完成。 this SO answer explains that DataTables do not hold any managed resources and suppress finalisation, so you should not need to call GC.WaitForPendingFinalizers for memory to be reclaimed. 这个SO答案解释了DataTables没有任何托管资源并且禁止最终化,所以你不需要调用GC.WaitForPendingFinalizers来回收内存。

Another possibility is that dt is not eligible for collection when GC.Collect is called - if there is a class-member or parent DataSet holding a reference to it, then it cannot be collected. 另一种可能性是,在GC.Collect时, dt不符合收集条件 - 如果有一个类成员或父DataSet持有对它的引用,则无法收集它。

In addition, contrary to some of the other answers, GC.Collect does force an immediate collection (not just a 'hint') - the documentation states: 此外,与其他一些答案相反, GC.Collect确实强制立即收集(而不仅仅是'提示') - 文档说明:

Forces an immediate garbage collection of all generations. 强制所有世代立即进行垃圾收集。

This article also says this is the case - in the 'Conditions for a garbage collection' section, one of the three possibilities is: 本文还说明了这种情况 - 在“垃圾收集条件”部分中,三种可能性之一是:

The GC.Collect method is called. 调用GC.Collect方法。 In almost all cases, you do not have to call this method, because the garbage collector runs continuously. 几乎在所有情况下,您都不必调用此方法,因为垃圾收集器会持续运行。 This method is primarily used for unique situations and testing. 此方法主要用于独特的情况和测试。

The documentation on garbage collection on .NET has ALWAYS stated that it makes no guarantees as to when collection ocurs. 关于.NET上的垃圾收集的文档总是说它不能保证何时收集。

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

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

http://www.simple-talk.com/dotnet/.net-framework/understanding-garbage-collection-in-.net/ - - This is a nice article for explaining garbage collection with nice diagrams to make it easier to grasp. http://www.simple-talk.com/dotnet/.net-framework/understanding-garbage-collection-in-.net/ - 这是一篇很好的文章,用于解释垃圾收集和漂亮的图表,使其更容易掌握。

excerpt from that last article relevant to your question: 摘自上一篇与您的问题相关的文章:

If an object has a finalizer, it is not immediately removed when the garbage collector decides it is no longer 'live'. 如果一个对象有一个终结器,当垃圾收集器决定它不再“活动”时,它不会被立即删除。 Instead, it becomes a special kind of root until .NET has called the finalizer method. 相反,它在.NET调用终结器方法之前成为一种特殊的根。 This means that these objects usually require more than one garbage collection to be removed from memory, as they will survive the first time they are found to be unused. 这意味着这些对象通常需要从内存中删除多个垃圾收集,因为它们将在第一次被发现未使用时存活。

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

Use this method to try to reclaim all memory that is inaccessible. 使用此方法尝试回收所有无法访问的内存。

All objects, regardless of how long they have been in memory, are considered for collection; 所有对象,无论它们在记忆中存在多长时间,都被考虑收集; however, objects that are referenced in managed code are not collected. 但是,不会收集托管代码中引用的对象。 Use this method to force the system to try to reclaim the maximum amount of available memory. 使用此方法强制系统尝试回收最大可用内存量。

Thus it may be possible that calling Collect() will not necessarily produce what you're expecting it to immediately produce. 因此,调用Collect()可能不一定会产生你期望它立即产生的东西。

Along with the previous answers about how garbage collection works the first access of the DataTable class allocates some static variables that are not going to be released when no instances of DataTable's exist. 除了之前关于垃圾收集如何工作的答案之外,DataTable类的第一次访问分配了一些静态变量,当没有DataTable实例存在时,这些变量不会被释放。 This can also apply to other classes depending on their implementation. 这也适用于其他类,具体取决于它们的实现。 Creating a DataTable and all the 创建一个DataTable和所有

Since you are accessing SQL, according to your GetSomeDataTableFromSql() method, you may have some cached SQL connection instances and other objects you don't directly know about. 由于您正在访问SQL,因此根据您的GetSomeDataTableFromSql()方法,您可能有一些缓存的SQL连接实例和您不直接了解的其他对象。

So allocating a DataTable instance and then getting rid of it will not get you back to the same level of memory allocation you had. 因此,分配一个DataTable实例然后摆脱它将不会让你回到你所拥有的相同级别的内存分配。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM