Yes, I know how to use GC.SuppressFinalize()
- it's explained here. I've read many times that using GC.SuppressFinalize()
removes the object from finalization queue and it's assumed this is good because it relieves the GC from extra work calling the finalizer.
So I crafted this (mostly useless) code where the class implements IDisposable
as in the linked to answer:
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;
}
}
}
Here I'm using Sleep(0)
to imitate some short unmanaged work. Note that because of boolean field in the class this unmanaged work is never executed more than once - even if I call Dispose()
multiple times or if the object is first disposed and then finalized - in any of these cases the "unmanaged work" is executed only once.
Here's the code I use for measurements:
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());
Yes, I add objects which were just disposed, into List
.
So I run the code above and it runs in 12.01 seconds (Release, without debugger). I then comment out the GC.SuppressFinalize()
call and run the code again and it runs in 13.99 seconds.
The code which calls GC.SuppressFinalize()
is 14.1 percent faster. Even in this ridiculous case where all is done to stress the GC (you rarely craft ten million objects with finalizer in row, don't you?) the difference is about 14%.
I guess that in realistic scenarios where just a fraction of objects has finalizers in the first place and those objects are also not created massively the difference in overall system performance would be negligible.
Am I missing something? Is there a realistic scenario where I would see notable benefits from using GC.SuppressFinalize()
?