C# destructor not called even after code scope + GC? [duplicate] - c#

This question already has answers here:
Understanding garbage collection in .NET
(2 answers)
Closed 2 years ago.
I've got a simple test code in Nunit+.net core 2.0 under VS2019:
public class Tests
{
public static int i = 0;
class First
{
~First()
{
i += 1;
Console.WriteLine("First's destructor is called.");
}
}
class Second : First
{
~Second() { i += 10; Console.WriteLine("Second's destructor is called."); }
}
class Third : Second
{
~Third() { i += 100; Console.WriteLine("Third's destructor is called."); }
}
[Test]
public static void Test()
{
{
Third t = new Third();
}
Thread.Sleep(1000);
System.GC.Collect();
Assert.AreEqual(111, i);
}
}
It always fails, while I found finally i=0, and the destructors are called right after Test(). But you can see the "t" is in a code block, and is invalid after the code block, and I also called
System.GC.Collect();
before my "Assert".
Why destructor is not called even after GC? How to fix my code to make the test pass?
Thanks a lot.

Use using statement and the IDisposable interface instead destructor;
public class Tests
{
public static int i = 0;
class First : IDisposable
{
public virtual void Dispose()
{
i += 1; Console.WriteLine("First's Dispose is called.");
}
}
class Second : First
{
public override void Dispose()
{
i += 10; Console.WriteLine("Second's Dispose is called.");
base.Dispose();
}
}
class Third : Second
{
public override void Dispose()
{
i += 100; Console.WriteLine("Third's Dispose is called.");
base.Dispose();
}
}
public static void Test()
{
using (Third t = new Third()) {
Console.WriteLine("Now everything will be ok, after leaving this block");
Console.WriteLine("t object will be dispose");
}
Thread.Sleep(1000);
System.GC.Collect();
Assert.AreEqual(111, i);
}
}
You can call System.GC.Collect() but it is not matter. The destructor will not be call immediately as you expect. (In above code you don't need System.GC.Collect())
It is strange to me why Microsoft does not make this clearer, instead of the old definition "Forces an immediate garbage collection of all generations." which leads developers to think they should expect an immediate result and GC.Collect() should wait for garbage collection to be complete before it returns?!
GC.Collect Method(System) - Microsoft Docs
Even more strange to me is that the code they give as an example for using of GC.Collect() don't work as they claim, for the same reason. You can try yourself by commenting GC.Collect(); line. I don't know, maybe they just don't update their documents as often. Maybe the situation was different before with GC.Collect()?! GC.CollectRequest() would be a more appropriate name for this function, if you ask me.
P.s. I'm new to C# so please, excuse me if I say things that do not fully understand or something I'm wrong in the code I write.

Call GC.WaitForPendingFinalizers() after GC.Collect()
The finalizers (not destructors) run as background tasks asynchronously, so after you call GC.Collect() and reach the assertion statement the finalizers are not guaranteed to have finished.

Related

Finalizer not being called after garbage collection

I have a test program that implements ManagedClass with both Dispose method and a Finalizer.
I'm forcing garbage collection via GC.Collect(), but Finalizer is only called if an object is created in a separate method. Why is that?
namespace FinalizerTest
{
internal class Program
{
static void Run()
{
ManagedClass mc = new ManagedClass();
mc.StartWriting();
}
static void Main(string[] args)
{
Console.WriteLine("Let's test Finalizers!");
// Test #1: Create object through another method
Console.WriteLine("Test #1");
Run();
GC.Collect();
GC.WaitForPendingFinalizers();
// Test #2: Create object directly in Main
Console.WriteLine("Test #2");
ManagedClass mc1 = new ManagedClass();
mc1.StartWriting();
mc1 = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.ReadLine();
}
}
public class ManagedClass : IDisposable
{
private StreamWriter _writer;
public void StartWriting()
{
_writer = new StreamWriter("output.txt");
}
public void Dispose()
{
Console.WriteLine("Disposing");
_writer?.Dispose();
GC.SuppressFinalize(this);
}
~ManagedClass()
{
Console.WriteLine("Finalizing");
_writer?.Dispose();
}
}
}
As I understand it in both scenarios a reference to the object is lost.
First test - reference is lost by exiting the scope of the method
Second test - reference is lost by setting reference variable to null
However, Finalizer is consistently only ever called after the first test.
According to Microsoft documentation:
The garbage collector then calls the Finalize method automatically under the following conditions:
After the garbage collector has discovered that an object is inaccessible, unless the object has been exempted from finalization by a call to the GC.SuppressFinalize method.
I'm not calling GCSuppressFinalize and according to Visual Studio I'm working on .NET 7.0
I tried:
Reassigning a new reference instead of null
Implementing Finalizer without the Dispose method
Running in Release configuration / building the project and running the application directly through exe file
None of those things called Test #2 Finalizer
Sometimes GC.Collect doesn't work perfectly.
To improve the garbage collection, I call the following procedure :
internal static void CollectGarbage(int SizeToAllocateInMo)
{ // set SizeToAllocateInMo to -1 to force garbage collection without testing available memory.
long [,] TheArray ;
bool DoCollect=SizeToAllocateInMo<0 ;
if (!DoCollect) try { TheArray = new long[SizeToAllocateInMo,125000] ; } catch { DoCollect=true ; }
TheArray=null ;
if (DoCollect)
{
GC.Collect() ;
GC.WaitForPendingFinalizers() ;
SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle,-1,-1) ;
GC.Collect() ;
}
}
[System.Runtime.InteropServices.DllImport("kernel32.dll", CharSet=CharSet.Auto, SetLastError=true)]
private static extern int SetProcessWorkingSetSize(IntPtr process, int minimumWorkingSetSize, int maximumWorkingSetSize); // used for garbage collection

can I use C# IDisposable to automate job when exit the scope?

Can I use IDisposable to do automate job when triggers in exiting the scope?
This case, I am using IDisposable to only do some job in the end of method, not 'disposing' resources.
This is one of my code :
public class ScopeTimer : IDisposable
{
private Stopwatch sw = new Stopwatch();
private string _logMessage;
public ScopeTimer(string logMessage = "")
{
sw.Start();
_logMessage = logMessage;
}
public void SetMessage(string logMessage)
{
_logMessage = logMessage;
}
void IDisposable.Dispose()
{
sw.Stop();
Logging.Logger.Log($"[{_logMessage}] takes {sw.ElapsedMilliseconds} ms.");
}
}
and I am using like this :
public void SomeMethod()
{
using var timer = new ScopeTimer();
// do some job
// when method finished, timer.Dispose() is called
}
At least now, this code looks working fine, but is this a safe way in other many circumstances?
Yes you can do that. Even Stephen Cleary does it in Nito.AsyncEx like:
SemaphoreSlim _syncRoot = new();
...
using var lockHandle = this._syncRoot.Lock();
// or async
using var lockHandle = await this._syncRoot.LockAsync();
But this is opinion based. In my opinion it helps to make code cleaner because you can setup and restore a state and both parts are next to each other. It's easy to see that you don't miss something.
The drawback is that you might create some instances that needs to be freed. That might create some pressure for the GC.
We created a struct (not a class) to wrap such cleanup code:
public readonly struct Finally : IDisposable
{
private readonly Action? _onDispose;
public Finally(Action onDispose)
{
_ = onDispose ?? throw new ArgumentNullException(nameof(onDispose));
this._onDispose = onDispose;
}
public static Finally Create(Action onDispose)
{
return new Finally(onDispose);
}
public void Dispose()
{
// Keep in mind that a struct can always be created using new() or default and
// in that cases _onDispose is null!
this._onDispose?.Invoke();
}
}
You can use this like:
this._childControl.BeginUpdate();
using var #finally = Finally.Create(this._childControl.EndUpdate);
// or
this._member.SomeEvent -= this.OnMemberSomeEvent;
using var #finally = Finally.Create(() => this._member.SomeEvent += this.OnMemberSomeEvent);
Yes the memory allocation for the Finally-struct is unnecessary and the second example creates a delegate. All that stuff needs time and memory. In applications where performance is a real issue it might be better to use the normal try...finally blocks.
But we're also using Linq and this does also need context objects and delegates and nobody cares.
In my opinion, obvious correctness is more important than performance, because a quick but wrong result doesn't help anyone.

Can .NET detect "irrelevant" code at runtime and garbage-collect objects in it?

Suppose I have a C# method like this
public void foo(){
Object very_large_object;
if (very_large_object.some_runtime_condition ()) {
do_something ();
return;
}
do_something_else (very_large_object);
}
In my case, do_something is an expensive method and may take some time to finish. When it's running, can .NET GC detects that do_something_else won't be called and thus very_larget_object is no longer relevant and garbage-collects it?
Yes it can. It's a common misconception that object lifetime is somehow related to the enclosing scope (for example that very_large_object can only be collected when foo scope ends). You can verify your particular case with simple application:
class Program {
static void Main() {
var o = new TestObject();
var test = Console.ReadKey();
if (test.Key == ConsoleKey.Enter) {
DoSomething();
Console.ReadKey();
return;
}
DoSomethingElse(o);
}
static void DoSomething() {
Console.WriteLine("doing something");
Thread.Sleep(500);
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine("GC ran");
}
static void DoSomethingElse(TestObject o) {
Console.WriteLine(o);
}
}
public class TestObject {
public TestObject() {
}
~TestObject() {
Console.WriteLine("finalizer running");
}
}
If you compile that in Release mode with optimizations enabled, it will output the following (after you press Enter in console):
doing something
finalizer running
GC ran
So it's clear that o was considered garbage and was put into finalizer queue while we were still inside DoSomething method. Optimizer could not just throw away branch with DoSomethingElse because it depends on your console input whether it will run or not. If you remove return then of course it won't be collected.

Calling method after destructor runs

I've readed about this, but I forgot, where I saw an example. So it looks like this
public class Program
{
private static void Main()
{
new SomeClass(10).Foo();
}
}
public class SomeClass
{
public int I;
public SomeClass(int input)
{
I = input;
Console.WriteLine("I = {0}", I);
}
~SomeClass()
{
Console.WriteLine("deleted");
}
public void Foo()
{
Thread.Sleep(2000);
Console.WriteLine("Foo");
}
}
so output should be:
I = 10
deleted
Foo
why? due to optimizer. It sees that method doesn't use any field so it can destroy an object before the method is called. So why it doesn't do it?
I'l post an example if i found it.
so i found the source. Pro .NET Performance: Sasha Goldshtein , Dima Zurbalev , Ido Flatow
Another problem has to do with the asynchronous nature of finalization
which occurs in a dedicated thread. A finalizer might attempt to
acquire a lock that is held by the application code, and the
application might be waiting for finalization to complete by calling
GC.WaitForPendingFinalizers(). The only way to resolve this issue is
to acquire the lock with a timeout and fail gracefully if it can’t be
acquired. Yet another scenario is caused by the garbage collector’s
eagerness to reclaim memory as soon as possible. Consider the
following code which represents a naïve implementation of a File class
with a finalizer that closes the file handle:
class File3
{
Handle handle;
public File3(string filename)
{
handle = new Handle(filename);
}
public byte[] Read(int bytes)
{
return Util.InternalRead(handle, bytes);
}
~File3()
{
handle.Close();
}
}
class Program
{
static void Main()
{
File3 file = new File3("File.txt");
byte[] data = file.Read(100);
Console.WriteLine(Encoding.ASCII.GetString(data));
}
}
This innocent piece of code can break in a very nasty manner. The Read
method can take a long time to complete, and it only uses the handle
contained within the object, and not the object itself. The rules for
determining when a local variable is considered an active root dictate
that the local variable held by the client is no longer relevant after
the call to Read has been dispatched. Therefore, the object is
considered eligible for garbage collection and its finalizer might
execute before the Read method returns! If this happens, we might be
closing the handle while it is being used, or just before it is used.
but i can't reproduce this behaviour
public void Foo()
{
Thread.Sleep(1000);
Console.WriteLine("Foo");
}
Methods that don't use any instance member of a class should be declared static. Which has several advantages, it is for one very helpful to a reader of the code. It unambiguously states that a method doesn't mutate the object state.
And for another has the great advantage that you'll now understand why there's no discrepancy in seeing the method running after the object got finalized. The GC just doesn't have any reason to keep this alive, there are no references left to the object when Foo() starts executing. So no trouble at all getting it collected and finalized.
You'll find more background info on how the jitter reports object references to the garbage collector in this answer.
Anyway, i found the way to reproduce it, i just should read more attentive :) :
public class Program
{
private static void Main()
{
new Thread(() =>
{
Thread.Sleep(100);
GC.Collect();
}).Start();
new SomeClass(10).Foo();
}
}
public class SomeClass
{
public int I;
public SomeClass(int input)
{
I = input;
Console.WriteLine("I = {0}", I);
}
~SomeClass()
{
Console.WriteLine("deleted");
}
public void Foo()
{
Thread.Sleep(1000);
Console.WriteLine("Foo");
}
}
so in this case destructor will be called before Foo method.
The problem is because you're using threading in Foo. You tell the code to wait for 1 second, but you don't tell it to wait for the second to be up before executing everything else. Therefore the original thread executes the destructor before Foo finishes.
A better way of writing Foo would be something like this:
public void Foo()
{
var mre = new ManualResetEvent(false);
mre.WaitOne(1000);
Console.WriteLine("Foo");
}
Using the ManualResetEvent will force the code to completely pause until, in this case, the timeout is hit. After which the code will continue.

how to dispose C# base class when derived class constructor generates an error

If a C# derived IDisposable class constructor generates an error, how to dispose of the already fully constructed IDisposable base class?
Since all fields in the class hierarchy are initialized before any constructor executes, is it safe for the derived constructor to call base.Dispose()? It violates the rule of not calling virtual methods until object is fully constructed, but I can't think of another way to do it, and my searching hasn't turned up anything about this scenario.
My view is that constructors should be light, not relying on external resources/etc that might throw an exception. The constructor should do enough to verify Dispose() can be safely called. Consider using containment instead of inheritance, or having a factory method do the work that might throw.
All derived class constructors must call Dispose on the object being constructed if they exit via an exception. Further, it's very difficult to write leak-proof classes if field initializers construct IDisposable instances or can fail. Too bad, since requiring objects to be declared in one place, initialized in a second, and cleaned up in a third is not exactly a recipe for coherent code.
I would suggest that the best pattern is probably something like:
class foo : baseFoo , IDisposable
{
foo () : baseFoo
{
bool ok = false;
try
{
do_stuff();
ok = true; // Only after last thing that can cause failure
}
finally
{
if (!ok)
Dispose();
}
}
}
Note that C++/CLI automatically implements that pattern, and can also automatically handle cleanup of IDisposable fields. Too bad the language seems like a pain in other ways.
PS--With relatively few exceptions, mostly revolving around predictable-cost shared immutable objects (e.g. brushes, fonts, small bitmaps, etc.), code which relies upon Finalize to clean up objects is broken. If an IDisposable is created, it must be disposed unless the code which created it has particular knowledge about the consequences of deferring disposal to a finalizer.
For Managed resources, this should not be a problem, they will get Garbage Collected. For Unmanaged resources, make sure you have a Finalizer defined for your object, that will ensure the Unmanaged resources are cleaned up.
Also, throwing exceptions from a constructor is considered very bad manners, It's better to provide a Factory Method to do the construction and error handling or to equip your object with an Initialize method that will throw the actual exception. That way the construction always succeeds and you're not left with these type of issues.
Correct, Dispose isn't called by the Garbage Collector, but a Finalizer is, which in turn needs to call Dispose. It's a more expensive technique, except when used correctly. I didn't say so in my answer, did I ;).
You can call into the GC to force a collection run and you can wait for all pending finalizers. It's still better to not put the exception generating code in the construstor or by placing a try/catch around the code in the constructor to ensure Dispose is called on these files in case of an error. You can always rethrow the exception later.
This solution builds on the proposal by #supercat. Initialization of any member that might throw must be performed in the constructor's try/catch block. With that condition met an exception thrown from any constructor will correctly dispose a fully or partially constructed base or derived class.
In this test code uncomment each of the four exceptions in turn and the program will output which of the Disposable resources were not disposed properly due to the constructor throwing an exception. Then uncomment the two Dispose calls and observe that everything gets cleaned up as it should.
class DisposableResource : IDisposable
{
public DisposableResource(string id) { Id = id; }
~DisposableResource() { Console.WriteLine(Id + " wasn't disposed.\n"); }
public string Id { get; private set; }
public void Dispose() { GC.SuppressFinalize(this); }
}
class Base : IDisposable
{
public Base()
{
try
{
throw new Exception(); // Exception 1.
_baseCtorInit = new DisposableResource("_baseCtorInit");
// throw new Exception(); // Exception 2.
}
catch(Exception)
{
// Dispose(); // Uncomment to perform cleanup.
throw;
}
}
public virtual void Dispose()
{
if (_baseFieldInit != null)
{
_baseFieldInit.Dispose();
_baseFieldInit = null;
}
if (_baseCtorInit != null)
{
_baseCtorInit.Dispose();
_baseCtorInit = null;
}
}
private DisposableResource _baseFieldInit = new DisposableResource("_baseFieldInit");
private DisposableResource _baseCtorInit;
}
class Derived : Base
{
public Derived()
{
try
{
// throw new Exception(); // Exception 3.
_derivedCtorInit = new DisposableResource("_derivedCtorInit");
// throw new Exception();
}
catch (Exception)
{
// Dispose(); // Uncomment to perform cleanup.
throw;
}
}
public override void Dispose()
{
if (_derivedFieldInit != null)
{
_derivedFieldInit.Dispose();
_derivedFieldInit = null;
}
if (_derivedCtorInit != null)
{
_derivedCtorInit.Dispose();
_derivedCtorInit = null;
}
base.Dispose();
}
private DisposableResource _derivedFieldInit = new DisposableResource("_derivedFieldInit");
private DisposableResource _derivedCtorInit;
}
class Program
{
static void Main(string[] args)
{
try
{
Derived d = new Derived();
}
catch (Exception)
{
Console.WriteLine("Caught Exception.\n");
}
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Console.WriteLine("\n\nPress any key to continue...\n");
Console.ReadKey(false);
}
}

Categories

Resources