更新(2009年12月1日):
我想修改这个答案,并承认原来的答案有缺陷。
最初的分析确实适用于需要最终确定的对象——没有准确、深入的理解,实践不应该在表面上被接受的观点仍然成立。
然而,数据集、数据视图、数据表在它们的构造函数中抑制了终结——这就是为什么显式地在它们上调用Dispose()不做任何事情。
据推测,这是因为它们没有非托管资源;因此,尽管MarshalByValueComponent允许使用非托管资源,但这些特定的实现不需要,因此可以放弃终结。
(. net作者会注意抑制通常占用最多内存的类型的终结,这说明了这种实践对于可终结类型的重要性。)
尽管如此,自从。net框架出现以来(大约8年前),这些细节仍然没有得到充分的记录,这是相当令人惊讶的(你基本上只能靠自己的设备来筛选相互冲突的、模糊的材料,然后把它们拼凑在一起,这有时令人沮丧,但确实提供了对我们每天所依赖的框架的更完整的理解)。
经过大量阅读,我的理解如下:
If an object requires finalization, it could occupy memory longer than it needs to – here’s why: a) Any type that defines a destructor (or inherits from a type that defines a destructor) is considered finalizable; b) On allocation (before the constructor runs), a pointer is placed on the Finalization queue; c) A finalizable object normally requires 2 collections to be reclaimed (instead of the standard 1); d) Suppressing finalization doesn’t remove an object from the finalization queue (as reported by !FinalizeQueue in SOS)
This command is misleading; Knowing what objects are on the finalization queue (in and of itself) isn’t helpful; Knowing what objects are on the finalization queue and still require finalization would be helpful (is there a command for this?)
Suppressing finalization turns a bit off in the object's header indicating to the runtime that it doesn’t need to have its Finalizer invoked (doesn’t need to move the FReachable queue); It remains on the Finalization queue (and continues to be reported by !FinalizeQueue in SOS)
DataTable, DataSet, DataView类都植根于MarshalByValueComponent,这是一个可以(潜在地)处理非托管资源的可终结对象
Because DataTable, DataSet, DataView don’t introduce unmanaged resources, they suppress finalization in their constructors
While this is an unusual pattern, it frees the caller from having to worry about calling Dispose after use
This, and the fact that DataTables can potentially be shared across different DataSets, is likely why DataSets don’t care to dispose child DataTables
This also means that these objects will appear under the !FinalizeQueue in SOS
However, these objects should still be reclaimable after a single collection, like their non-finalizable counterparts
4(新参考资料):
http://www.devnewsgroups.net/dotnetframework/t19821-finalize-queue-windbg-sos.aspx
http://blogs.msdn.com/tom/archive/2008/04/28/asp-net-tips-looking-at-the-finalization-queue.aspx
http://issuu.com/arifaat/docs/asp_net_3.5unleashed
http://msdn.microsoft.com/en-us/magazine/bb985013.aspx
http://blogs.msdn.com/tess/archive/2006/03/27/561715.aspx
最初的回答:
关于这个问题有很多误导性的答案,通常都很糟糕——任何登陆这里的人都应该忽略噪音,仔细阅读下面的参考资料。
毫无疑问,Dispose应该在任何可终结对象上被调用。
我被冒犯了
调用Dispose可以显著加快内存的回收速度。
MarshalByValueComponent在Dispose()中调用GC.SuppressFinalize(this)——跳过这个意味着在内存回收之前必须等待几十个(如果不是数百个)Gen0集合:
With this basic understanding of finalization we
can already deduce some very important
things:
First, objects that need finalization
live longer than objects that do not.
In fact, they can live a lot longer.
For instance, suppose an object that
is in gen2 needs to be finalized.
Finalization will be scheduled but the
object is still in gen2, so it will
not be re-collected until the next
gen2 collection happens. That could be
a very long time indeed, and, in fact,
if things are going well it will be a
long time, because gen2 collections
are costly and thus we want them to
happen very infrequently. Older
objects needing finalization might
have to wait for dozens if not
hundreds of gen0 collections before
their space is reclaimed.
Second, objects that need finalization
cause collateral damage. Since the
internal object pointers must remain
valid, not only will the objects
directly needing finalization linger
in memory but everything the object
refers to, directly and indirectly,
will also remain in memory. If a huge
tree of objects was anchored by a
single object that required
finalization, then the entire tree
would linger, potentially for a long
time as we just discussed. It is
therefore important to use finalizers
sparingly and place them on objects
that have as few internal object
pointers as possible. In the tree
example I just gave, you can easily
avoid the problem by moving the
resources in need of finalization to a
separate object and keeping a
reference to that object in the root
of the tree. With that modest change
only the one object (hopefully a nice
small object) would linger and the
finalization cost is minimized.
Finally, objects needing finalization
create work for the finalizer thread.
If your finalization process is a
complex one, the one and only
finalizer thread will be spending a
lot of time performing those steps,
which can cause a backlog of work and
therefore cause more objects to linger
waiting for finalization. Therefore,
it is vitally important that
finalizers do as little work as
possible. Remember also that although
all object pointers remain valid
during finalization, it might be the
case that those pointers lead to
objects that have already been
finalized and might therefore be less
than useful. It is generally safest to
avoid following object pointers in
finalization code even though the
pointers are valid. A safe, short
finalization code path is the best.
从那些在第2代中看过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-finali zedispose-pattern.aspx
3 -
http://codeidol.com/csharp/net-framework/Inside-the-CLR/Automatic-Memory-Management/