C# 在Dispose中使用/不使用SuppressFinalize的终结器的开销
假设:C# 在Dispose中使用/不使用SuppressFinalize的终结器的开销,c#,dispose,idisposable,finalizer,suppressfinalize,C#,Dispose,Idisposable,Finalizer,Suppressfinalize,假设: 类只管理成员 一些成员实现了IDisposable 类是密封的-类不能从非托管资源派生和添加非托管资源 对象在语句中使用,即Dispose()完成后调用 对于此类,IDisposable有3种可能的实现: 调用IDisposable成员上的Dispose()的最小Dispose方法-无终结器 标准的IDisposable带有终结器的实现,但是缺少了通常的GC.SuppressFinalize(this)调用Dispose() 使用Finalizer(以及使用GC.SuppressFi
- 类只管理成员
- 一些成员实现了
IDisposable
- 类是密封的-类不能从非托管资源派生和添加非托管资源李>
- 对象在
语句中使用,即
完成后调用Dispose()
IDisposable
有3种可能的实现:
IDisposable
成员上的Dispose()
的最小Dispose
方法-无终结器李>
IDisposable
带有终结器的实现,但是缺少了通常的GC.SuppressFinalize(this)
调用Dispose()
GC.SuppressFinalize(this)
调用Dispose()
)的完整标准IDisposable
实现SuppressFinalize
,避免了终结器开销”——但我认为(,并想澄清一下)这是不正确的。终结器队列中的对象的开销仍然会发生-您所要避免的只是实际的终结器调用-在常见情况下,这只是“我已被处置,不做任何事情”
注意:这里所说的“完整标准IDisposable
implementation”是指设计用于涵盖非托管和托管资源情况的标准实现(注意,这里我们只有托管对象成员)
您应该只在需要清理非托管资源的对象上包含终结器。由于您只有托管成员,因此不需要终结器-如果成员具有终结器并且没有为其调用
GC.SuppressFinalize()
,则终结器将在成员本身上运行
终结器的目标是在GC需要时清理非托管资源及其托管包装,而Dispose
模式的目标是在特定时刻清理任何类型的资源
没有人应该认为“我通过调用SuppressFinalize避免了终结器开销”——相反,他们应该认为“我已经清理了我的非托管资源,没有必要运行终结器”
关于编号问题:
Dispose()
不同版本的.NET GC可能会做不同的事情,但据我所知,任何带有
Finalize
方法的对象都将被添加到“finalizer队列”(如果放弃,则已请求通知的对象列表)中,并且只要该队列存在,就会一直保留在该队列中。取消注册和重新注册以进行终结的方法(IMHO应该是对象
的受保护成员)在对象头中设置或清除一个标志,该标志控制对象是否应移动到“freachable队列”(其finalize
方法应尽快运行的对象列表)如果发现该对象已被放弃,但不会导致从终结器队列中添加或删除该对象
因此,覆盖Finalize
的每种类型的每个实例都会对它所参与的每个垃圾收集周期(只要它存在)施加一个很小但非零的开销。在放弃对象之前对其调用SuppressFinalize
,将阻止其移动到freachable队列,但不会消除因其在整个存在过程中一直处于可终止队列中而产生的开销
我建议,任何面向公众的对象都不应该实现
Finalize
。Finalize
方法有一些合法的用途,但是重写它的类应该避免保留对Finalize不需要的任何东西的引用(有点令人烦恼的是,如果可终结对象持有对弱引用的唯一引用,则在终结器运行之前,弱引用可能会失效,即使弱引用的目标仍处于活动状态).我有时会使用终结器进行调试,以检查我是否遗漏了一些dispose。如果有人感兴趣,我会在我的系统上进行快速测试,以检查性能影响(Windows 10、.Net 4.7.1、Intel Core i5-8250U)
添加终结器并抑制它的成本约为每个对象60 ns,添加终结器并忘记调用dispose的成本约为每个对象800 ns
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);
}
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);
}
}
}
}