I have a simple class that is defined as below.
public class Person
{
public Person()
{
}
public override string ToString()
{
return "I Still Exist!";
}
~Person()
{
p = this;
}
public static Person p;
}
In Main method
public static void Main(string[] args)
{
var x = new Person();
x = null;
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(Person.p == null);
}
Is garbage collector supposed to main reference for Person.p and when exactly will the destructor be called?
What is Garbage Collector? Garbage collector looks for an instance/object which is not required anymore and treats these objects/instances as ready to destroy. Then, it makes a call to the destructor to release the memory and deallocate the resources.
In the common language runtime (CLR), the garbage collector (GC) serves as an automatic memory manager. The garbage collector manages the allocation and release of memory for an application. For developers working with managed code, this means that you don't have to write code to perform memory management tasks.
Because Java is a garbage collected language you cannot predict when (or even if) an object will be destroyed. Hence there is no direct equivalent of a destructor.
The garbage collector is a part of the . NET environment that keeps track of objects and makes sure that objects are removed from memory when they are no longer needed. A destructor is a part of a class design. It's the opposite of a constructor.
The thing you are missing here is that the compiler is extending the lifetime of your x
variable until the end of the method in which it is defined - that's just something the compiler does - but it only does it for a DEBUG build.
If you change the code so that the variable is defined in a separate method, it will work as you expect.
The output of the following code is:
False
True
And the code:
using System;
namespace ConsoleApp1
{
class Finalizable
{
~Finalizable()
{
_extendMyLifetime = this;
}
public static bool LifetimeExtended => _extendMyLifetime != null;
static Finalizable _extendMyLifetime;
}
class Program
{
public static void Main()
{
test();
Console.WriteLine(Finalizable.LifetimeExtended); // False.
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(Finalizable.LifetimeExtended); // True.
}
static void test()
{
new Finalizable();
}
}
}
So basically your understanding was correct, but you didn't know that the sneaky compiler was going to keep your variable alive until after you called GC.Collect()
- even if you explicitly set it to null!
As I noted above, this only happens for a DEBUG build - presumably so you can inspect the values for local variables while debugging to the end of the method (but that's just a guess!).
The original code DOES work as expected for a release build - so the following code outputs false, true
for a RELEASE build and false, false
for a DEBUG build:
using System;
namespace ConsoleApp1
{
class Finalizable
{
~Finalizable()
{
_extendMyLifetime = this;
}
public static bool LifetimeExtended => _extendMyLifetime != null;
static Finalizable _extendMyLifetime;
}
class Program
{
public static void Main()
{
new Finalizable();
Console.WriteLine(Finalizable.LifetimeExtended); // False.
GC.Collect();
GC.WaitForPendingFinalizers();
Console.WriteLine(Finalizable.LifetimeExtended); // True iff RELEASE build.
}
}
}
As an addendum: Note that if you do something in the finalizer for a class that causes a reference to the object being finalized to be reachable from a program root, then that object will NOT be garbage-collected unless and until that object is no longer referenced.
In other words, you can give an object a "stay of execution" via the finalizer. This is generally held to be a bad design, though!
For example, in the code above, where we do _extendMyLifetime = this
in the finalizer, we are creating a new reference to the object, so it will not now be garbage-collected until _extendMyLifetime
(and any other reference) no longer references it.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With