更新(2009年12月1日):
我想修改此答案,并承认原始答案有缺陷。
原始分析的确适用于需要最终确定的对象-而且一点仍然是,如果没有准确,深入的理解,就不应在表面上接受实践。
但是,事实证明,数据集,数据视图,数据表会抑制其构造函数中的终结处理–这就是为什么在它们上显式调用Dispose()不会执行任何操作的原因。
据推测,发生这种情况是因为他们没有不受管的资源;因此,尽管MarshalByValueComponent允许对非托管资源进行了分配,但这些特定的实现并不需要,因此可以放弃最终确定。
(.NET作者会小心翼翼地抑制通常占用最多内存的那些类型的最终确定,这说明这种做法通常对于可终结类型很重要。)
尽管如此,自.NET Framework诞生(将近8年前)以来,这些细节仍未得到充分的文档记录(令人惊讶的是,实际上,您留给自己的设备来筛选冲突,模棱两可的材料以将各个部分放在一起)有时会令人沮丧,但确实提供了对我们每天依赖的框架的更完整的理解)。
经过大量阅读,这是我的理解:
如果对象需要终结处理,那么它可能会占用比所需时间更长的内存–这就是原因:a)任何定义了析构函数的类型(或从定义了析构函数的类型继承的类型)都被视为可终结的;b)在分配时(在构造函数运行之前),将指针放置在完成队列上;c)可终结对象通常需要回收2个集合(而不是标准1);d)禁止终结处理不会从终结处理队列中删除对象(如SOS中的!FinalizeQueue所报告)。知道终结队列上的对象本身(本身)并没有帮助;知道哪些对象在终结队列中并且仍然需要终结将很有帮助(是否有此命令?)
取消终结处理会使对象的标头中的位关闭一点,从而向运行时指示它不需要调用其终结器(不需要移动FReachable队列);它保留在完成队列中(并继续由SOS中的!FinalizeQueue报告)。
DataTable,DataSet和DataView类都植根于MarshalByValueComponent,MarshalByValueComponent是一个可终结的对象,可以(潜在地)处理非托管资源
- 因为DataTable,DataSet,DataView不会引入非托管资源,所以它们抑制了构造函数中的最终确定
- 尽管这是一个不寻常的模式,但它使调用者不必担心在使用后调用Dispose。
- 这以及DataTables可能在不同的DataSet之间共享的事实,很可能是为什么DataSet不在意处置子DataTables的原因
- 这也意味着这些对象将出现在SOS的!FinalizeQueue下
- 但是,这些对象在一次收集后仍应可回收,就像它们不可最终完成的对等一样
4(新参考):
原始答案:
关于此问题有很多误导且通常很差的答案-降落在此处的任何人都应该忽略噪音,并仔细阅读以下参考资料。
毫无疑问,应该在任何Finalizable对象上调用Dispose 。
数据表是可终结的。
调用Dispose可以显着加快内存回收速度。
MarshalByValueComponent在其Dispose()中调用GC.SuppressFinalize(this) -跳过这意味着在回收内存之前必须等待数十个(即使不是数百个)Gen0集合:
有了对完成的基本理解,我们已经可以推断出一些非常重要的事情:
首先,需要终结处理的对象比不需要终结处理的对象生存更长的时间。实际上,它们的寿命更长。例如,假设第2代中的一个对象需要完成。终结将被安排,但该对象仍在gen2中,因此直到下一个gen2收集发生之前,它不会被重新收集。确实,这可能是很长的时间,而且实际上,如果一切顺利,那将是很长的时间,因为第二代收集成本很高,因此我们希望它们很少出现。需要回收的较旧对象可能需要等待数十个(即使不是数百个)gen0集合,然后才能回收它们的空间。
其次,需要最终确定的对象会造成附带损害。由于内部对象指针必须保持有效,因此不仅直接需要终结处理的对象将在内存中徘徊,而且对象直接或间接引用的所有内容也将保留在内存中。如果一棵巨大的对象树由需要终结的单个对象锚定,则整个树将徘徊不前,可能就像我们刚才讨论的那样持续很长时间。因此,重要的是要谨慎使用终结器,并将其放置在内部对象指针尽可能少的对象上。在我刚才给出的树示例中,您可以通过将需要完成的资源移动到一个单独的对象并将对该对象的引用保留在树的根中来轻松避免该问题。
最后,需要终结处理的对象为终结器线程创建工作。如果终结处理是一个复杂的过程,那么唯一的终结处理线程将花费大量时间来执行这些步骤,这可能导致工作积压,并导致更多的对象徘徊在等待终结的过程。因此,终结器完成尽可能少的工作至关重要。还要记住,尽管在完成过程中所有对象指针仍然有效,但可能是因为这些指针指向已经完成的对象,因此可能没有用。通常,即使指针有效,也要避免在终结代码中跟随对象指针。安全,简短的最终代码路径是最好的。
从在Gen2中看到100 MB的未引用DataTables的人那里获取数据:这非常重要,并且该线程的答案完全将其遗漏了。
参考文献:
1 -
http://msdn.microsoft.com/en-us/library/ms973837.aspx
2 -
http
: //vineetgupta.spaces.live.com/blog/cns!8DE4BDC896BEE1AD!1104.entry http://www.dotnetfunda.com/articles/article524-net-best-practice-no-2-improve-garbage收集器性能使用finalizedispose-pattern.aspx
3 -
http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/