请问 GC.SuppressFinalize 有什么实质性的影响吗?
May I have a case where GC.SuppressFinalize has any substantial effect please?
是的,我知道 如何 使用 GC.SuppressFinalize()
- 它已被解释 here。我读过很多次,使用 GC.SuppressFinalize()
从终结队列中删除对象,我认为这是好的,因为它使 GC 免于调用终结器的额外工作。
所以我制作了这段(大部分没用的)代码,其中 class 实现了 IDisposable
,如链接中的答案:
public class MyClass : IDisposable
{
~MyClass()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
System.Threading.Thread.Sleep(0);
disposed = true;
}
}
}
我在这里使用 Sleep(0)
来模仿一些简短的非托管工作。请注意,由于 class 中的布尔字段,此非托管工作永远不会执行超过一次 - 即使我多次调用 Dispose()
或者如果对象首先被处置然后完成 - 在任何这些情况下"unmanaged work" 只执行一次。
这是我用于测量的代码:
var start = DateTime.UtcNow;
var objs = new List<Object>();
for (int i = 0; i < 1000 * 1000 * 10; i++)
{
using (var obj = new MyClass())
{
objs.Add(obj);
}
}
objs = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
var duration = (DateTime.UtcNow - start).TotalMilliseconds;
Console.WriteLine(duration.ToString());
是的,我将刚刚处理的对象添加到 List
。
所以我 运行 上面的代码和它 运行s 在 12.01 秒内(发布,没有调试器)。然后我再次注释掉 GC.SuppressFinalize()
调用和 运行 代码,它在 13.99 秒内 运行s。
调用 GC.SuppressFinalize()
的代码快了 14.1%。即使在这种荒谬的情况下,所有的一切都是为了给 GC 施加压力(你很少用连续的终结器来制作一千万个对象,不是吗?)差异大约是 14%。
我猜想在只有一小部分对象首先具有终结器并且这些对象也没有大量创建的现实场景中,整体系统性能的差异可以忽略不计。
我错过了什么吗?有没有一个现实的场景,我会看到使用 GC.SuppressFinalize()
的显着好处?
举个简单的例子:
protected virtual void Dispose(bool disposing)
{
if(!disposing)
{
HardStopSystem(
$"Critical failure: an instance of '{GetType().Name}' was not disposed");
}
}
这只有在命中终结器时才会达到。是的,它是强制的和人为的,但是:这是基于我在许多系统中看到的真实代码,在这些系统中,正确处理事物是至关重要。
另一个例子包括确保非托管指针之类的东西不会被释放两次(这会失败)。现在是的,你可以设置一个标记说 "don't do this again",但是你会进入不可变类型等话题
the difference in overall system performance would be negligible.
这在很大程度上取决于场景。最终,终结器更多的是关于正确性而不是性能。 IDisposable
与性能更相关,因为它与及时性有关。
是的,我知道 如何 使用 GC.SuppressFinalize()
- 它已被解释 here。我读过很多次,使用 GC.SuppressFinalize()
从终结队列中删除对象,我认为这是好的,因为它使 GC 免于调用终结器的额外工作。
所以我制作了这段(大部分没用的)代码,其中 class 实现了 IDisposable
,如链接中的答案:
public class MyClass : IDisposable
{
~MyClass()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
System.Threading.Thread.Sleep(0);
disposed = true;
}
}
}
我在这里使用 Sleep(0)
来模仿一些简短的非托管工作。请注意,由于 class 中的布尔字段,此非托管工作永远不会执行超过一次 - 即使我多次调用 Dispose()
或者如果对象首先被处置然后完成 - 在任何这些情况下"unmanaged work" 只执行一次。
这是我用于测量的代码:
var start = DateTime.UtcNow;
var objs = new List<Object>();
for (int i = 0; i < 1000 * 1000 * 10; i++)
{
using (var obj = new MyClass())
{
objs.Add(obj);
}
}
objs = null;
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
var duration = (DateTime.UtcNow - start).TotalMilliseconds;
Console.WriteLine(duration.ToString());
是的,我将刚刚处理的对象添加到 List
。
所以我 运行 上面的代码和它 运行s 在 12.01 秒内(发布,没有调试器)。然后我再次注释掉 GC.SuppressFinalize()
调用和 运行 代码,它在 13.99 秒内 运行s。
调用 GC.SuppressFinalize()
的代码快了 14.1%。即使在这种荒谬的情况下,所有的一切都是为了给 GC 施加压力(你很少用连续的终结器来制作一千万个对象,不是吗?)差异大约是 14%。
我猜想在只有一小部分对象首先具有终结器并且这些对象也没有大量创建的现实场景中,整体系统性能的差异可以忽略不计。
我错过了什么吗?有没有一个现实的场景,我会看到使用 GC.SuppressFinalize()
的显着好处?
举个简单的例子:
protected virtual void Dispose(bool disposing)
{
if(!disposing)
{
HardStopSystem(
$"Critical failure: an instance of '{GetType().Name}' was not disposed");
}
}
这只有在命中终结器时才会达到。是的,它是强制的和人为的,但是:这是基于我在许多系统中看到的真实代码,在这些系统中,正确处理事物是至关重要。
另一个例子包括确保非托管指针之类的东西不会被释放两次(这会失败)。现在是的,你可以设置一个标记说 "don't do this again",但是你会进入不可变类型等话题
the difference in overall system performance would be negligible.
这在很大程度上取决于场景。最终,终结器更多的是关于正确性而不是性能。 IDisposable
与性能更相关,因为它与及时性有关。