假设如下:
IDisposable
。sealed
- 类无法派生并添加非托管资源。 using
语句中使用 - 即完成后调用Dispose()
。此类有IDisposable
的3种可能实现:
Dispose
成员上调用Dispose()
的最小IDisposable
方法 - 无终结器。 IDisposable
具有终结器但缺失的实施GC.SuppressFinalize(this)
中通常的Dispose()
来电。IDisposable
实施(并GC.SuppressFinalize(this)
调用Dispose()
)。以下陈述是否正确?我是否正确理解了这一点?
SuppressFinalize
,所以终结器将不会运行。这种情况仍会导致对象进入终结器队列的开销很小,但终结器实际上并没有运行。这里的关键点是,很有可能认为“我通过调用SuppressFinalize
避免了终结器开销” - 但我认为(并希望澄清)是不正确的。在终结器队列中的对象的开销仍然会发生 - 你要避免的只是实际的终结器调用 - 在通常情况下只是“我已经处理掉了”。
注意:这里的“完全标准IDisposable
实现”是指旨在涵盖非托管和托管资源案例的标准实现(请注意,此处我们只有托管对象成员)。
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
private bool _disposed;
protected virtual void Dispose(bool disposing) {
if (_disposed)
return;
if (disposing) {
// dispose managed members...
}
_disposed = true;
}
~AXCProcessingInputs() {
Dispose(false);
}
答案 0 :(得分:2)
不同版本的.NET GC可能会有不同的做法,但根据我的理解,任何带有Finalize
方法的对象都会被添加到"终结器队列" (如果放弃请求通知的对象列表),并且只要它存在就会保留在该队列中。取消注册和重新注册以进行最终化的方法(IMHO应该是Object
的受保护成员)设置或清除对象标题中的标志,该标志控制是否应将对象移动到"可释放队列&#34 ; (finalize
方法应该尽快运行的对象列表)如果发现它被放弃,但不会导致在终结器队列中添加或删除该对象。
每个覆盖Finalize
的类型的每个实例都会对它所参与的每个垃圾收集周期(只要它存在)施加一个很小但非零的开销。在放弃对象之前调用SuppressFinalize
将阻止它被移动到可释放的队列,但是不会消除由于它在整个存在期间处于可终结队列中而产生的开销。
我建议没有面向公众的对象应该实现Finalize
。 Finalize
方法有一些合法的用法,但覆盖它的类应该避免保留对最终化不需要的任何东西的引用(有点烦人,如果可终结对象持有对弱引用的唯一引用,弱引用可能是在终结器运行之前无效,即使弱引用的目标仍然存在)。
答案 1 :(得分:1)
您应该只在需要清理非托管资源的对象上包含终结器。由于您只有托管成员,因此您不需要终结器 - 如果终结器具有终结器并且未调用GC.SuppressFinalize()
,则终结器将自动运行。
终结器的目标是在GC感觉到时清理非托管资源及其托管包装器,而Dispose
模式的目标是清理上的任何类型的资源特定时刻。
没有人会想到"我通过调用SuppressFinalize"来避免终结器开销。 - 相反,他们应该思考"我已经清理了我的非托管资源,不需要终结器运行"。
关于编号问题:
Dispose()
答案 2 :(得分:0)
我有时将终结器用于调试目的,以检查是否缺少某些处置方法。如果有人感兴趣,我会在系统上进行快速测试以检查对性能的影响(Windows 10,.Net 4.7.1,Intel Core i5-8250U)。
添加终结器并抑制它的成本大约为每个对象60 ns ,添加终结器并忘记调用处置的成本大约为每个对象800 ns 。对性能的影响与调试/发行版以及有/没有附加调试器非常一致,这可能是因为两个版本中的垃圾收集器是相同的。
添加终结器并抑制终结器对性能的影响是最小的,除非要构造大量的这些对象,通常情况并非如此。甚至Microsoft自己的Task
都使用终结器(几乎总是被抑制),并且该类具有非常轻的性能。因此,他们显然同意。
但是,让终结器运行可能会变得很糟糕。考虑到我的测试用例使用了一个没有引用对象的琐碎类,并且它已经慢了一个数量级。拥有大量被引用的对象应该花费更多,因为所有这些对象都需要保持生命,才能再一代。这也可能导致在垃圾回收的压缩阶段发生大量复制。
测试的源代码:
using System;
using System.Diagnostics;
namespace ConsoleExperiments
{
internal class Program
{
private static void Main(string[] args)
{
GenerateGarbageNondisposable();
GenerateGarbage();
GenerateGarbageWithFinalizers();
GenerateGarbageFinalizing();
var sw = new Stopwatch();
const int garbageCount = 100_000_000;
for (var repeats = 0; repeats < 4; ++repeats)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
sw.Restart();
for (var i = 0; i < garbageCount; ++i)
GenerateGarbageNondisposable();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("Non-disposable: " + sw.ElapsedMilliseconds.ToString());
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
sw.Restart();
for (var i = 0; i < garbageCount; ++i)
GenerateGarbage();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("Without finalizers: " + sw.ElapsedMilliseconds.ToString());
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
sw.Restart();
for (var i = 0; i < garbageCount; ++i)
GenerateGarbageWithFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("Suppressed: " + sw.ElapsedMilliseconds.ToString());
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
sw.Restart();
for (var i = 0; i < garbageCount; ++i)
GenerateGarbageFinalizing();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("Finalizing: " + sw.ElapsedMilliseconds.ToString());
Console.WriteLine();
}
Console.ReadLine();
}
private static void GenerateGarbageNondisposable()
{
var bla = new NondisposableClass();
}
private static void GenerateGarbage()
{
var bla = new UnfinalizedClass();
bla.Dispose();
}
private static void GenerateGarbageWithFinalizers()
{
var bla = new FinalizedClass();
bla.Dispose();
}
private static void GenerateGarbageFinalizing()
{
var bla = new FinalizedClass();
}
private class NondisposableClass
{
private bool disposedValue = false;
}
private class UnfinalizedClass : IDisposable
{
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
}
disposedValue = true;
}
}
public void Dispose()
{
Dispose(true);
}
}
private class FinalizedClass : IDisposable
{
private bool disposedValue = false;
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
}
disposedValue = true;
}
}
~FinalizedClass()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
}
}