更新(2009 年 12 月 1 日):
我想修改这个答案并承认原来的答案有缺陷。
原始分析确实适用于需要最终确定的对象 - 并且在没有准确、深入的理解的情况下不应该在表面上接受实践这一点站立。
然而,事实证明 DataSets、DataViews、DataTables 在它们的构造函数中抑制了终结——这就是为什么在它们上显式调用 Dispose() 什么都不做的原因。
大概是因为它们没有非托管资源;因此,尽管 MarshalByValueComponent 允许使用非托管资源,但这些特定的实现不需要,因此可以放弃最终确定。
(.NET 作者会注意抑制通常占用最多内存的类型的终结性,这说明了这种做法对于可终结类型的重要性。)
尽管如此,自从 .NET 框架(大约 8 年前)问世以来,这些细节仍然没有被充分记录,这非常令人惊讶(您基本上只能靠自己的设备来筛选虽然相互冲突、模棱两可的材料)拼凑在一起有时令人沮丧,但确实提供了对我们日常所依赖的框架的更完整的理解)。
经过大量阅读,这是我的理解:
如果一个对象需要终结,它可能占用的内存比它需要的时间长——原因如下:a) 任何定义析构函数的类型(或从一个类型继承定义析构函数)被认为是可终结的; b) 在分配时(在构造函数运行之前),一个指针被放置在终结队列上; c) 一个可终结的对象通常需要 2 个集合 才能被回收(而不是标准的 1 个); d) 抑制终结不会从终结队列中删除对象(由 SOS 中的 !FinalizeQueue 报告)
该命令具有误导性;知道最终队列中的对象(本身)是没有帮助的;知道什么对象在终结队列中并且仍然需要终结将是有帮助的(有这个命令吗?)
抑制终结会在对象的标头中关闭一点,向运行时指示它不需要调用其终结器(不需要移动 FReachable 队列);它保留在 Finalization 队列中(并继续由 SOS 中的 !FinalizeQueue 报告)
DataTable、DataSet、DataView 类都植根于 MarshalByValueComponent,这是一个可以(可能)处理非托管资源的可终结对象
- 由于 DataTable、DataSet、DataView 不会引入非托管资源,它们会在其构造函数中抑制终结
- 虽然这是一种不寻常的模式,但它使调用者不必担心在使用后调用 Dispose
- 这一点,以及 DataTable 可能在不同 DataSet 之间共享的事实,可能是 DataSet 不关心处置子 DataTable 的原因
- 这也意味着这些对象会出现在 SOS 中的 !FinalizeQueue 下
- 但是,这些对象在单次收集后仍应可回收,就像它们不可终结的对应对象一样
4(新参考):
原答案:
对此有很多误导性且通常非常糟糕的答案 - 任何来到这里的人都应该忽略噪音并仔细阅读下面的参考资料。
毫无疑问,Dispose 应该在任何 Finalizable 对象上调用。
数据表是可终结的。
调用 Dispose 显着加速了内存的回收。
MarshalByValueComponent 在其 Dispose() 中调用 GC.SuppressFinalize(this) - 跳过这意味着在回收内存之前必须等待数十甚至数百个 Gen0 集合:
有了对最终确定的基本了解,我们
已经可以推断出一些非常重要的
事情:
首先,需要完成的对象
比没有寿命的物体寿命更长。
事实上,他们可以活得更久。
例如,假设一个对象
在 gen2 中需要最终确定。
定稿将被安排,但
对象仍在 gen2 中,所以它会
直到下一次才能重新收集
gen2 收集发生。那可能是
确实很长一段时间,事实上,
如果事情进展顺利,这将是一个
很长一段时间,因为 gen2 集合
是昂贵的,因此我们希望他们
很少发生。年长者
需要完成的对象可能
如果没有,必须等待几十个
之前的数百个 gen0 集合
他们的空间被回收了。
二、需要终结的对象
造成附带损害。由于
内部对象指针必须保留
有效,不仅将对象
直接需要finalization linger
在内存中,但一切都是对象
直接和间接地指,
也会留在记忆中。如果一个巨大的
对象树由 a 锚定
需要的单个对象
完成,然后是整个树
会徘徊,可能会持续很长时间
我们刚才讨论的时间。它是
因此使用终结器很重要
谨慎地将它们放在物体上
具有尽可能少的内部对象
尽可能多的指针。在树上
我刚刚给出的例子,你可以很容易地
通过移动来避免问题
需要最终确定的资源
单独的对象并保留一个
在根目录中引用该对象
树的。伴随着那适度的改变
只有一个对象(希望是一个不错的
小物体)会流连忘返
最终化成本最小化。
最后,需要完成的对象
为终结器线程创建工作。
如果您的最终确定过程是
复杂的,唯一的
终结器线程将花费
很多时间执行这些步骤,
这可能会导致工作积压
因此导致更多的物体逗留
等待最终确定。所以,
至关重要的是
终结者所做的工作尽可能少
可能的。还要记住,虽然
所有对象指针保持有效
在最终确定期间,它可能是
这些指针导致的情况
已经存在的对象
最终确定,因此可能会更少
比有用。通常最安全的是
避免跟随对象指针
完成代码,即使
指针有效。一个安全、短
终结代码路径是最好的。
从在 Gen2 中看到 100 MB 的非引用数据表的人那里得到它:这非常重要,并且完全被此线程上的答案所忽略。
参考资料:
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-collector-performance-using-finalizedispose-pattern.aspx
3 -
http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/