I've read this article and this too.
I was trying to implement destructor in simple code.
class Program
{
static void Main(string[] args)
{
CreateSubscriber();
Console.Read();
}
static void CreateSubscriber()
{
Subscriber s = new Subscriber();
s.list.Clear();
}
}
public class Subscriber
{
public List<string> list = new List<string>();
public Subscriber()
{
for(long i = 0; i < 1000000; i++)
{
list.Add(i.ToString());
}
}
~Subscriber()
{
//this line is only performed on explicit GC.Collect()
Console.WriteLine("Destructor Called - Sub");
}
}
As when code reached line of Console.Read()
, instance of Subscriber
was no longer in scope (I was expecting it to be eligible for Garbage collection). I left above code running for almost 2 hours waiting for destructor
of Subscriber
. but that never called, neither memory taken by the code released.
I understood, in c# we cannot call destructors programmatic and it is automatically called on Garbage collection
, So I tried to call GC.Collect()
explicitly.
By doing that, I could see that destructor was called. So In my above code, Garbage collection was not being done! But Why?
Is it because, program is single threaded and that thread is waiting for user input on Console.Read()
?
Or it does have something with List of string ? if so what is it
Update (for future readers)
as Fabjan suggested in his answer
Most likely somewhere when a new object is created and memory is allocated for it, GC does perform a check of all references and collects the first object.
and suggested to try
CreateSubscriber();
Console.Read();
CreateSubscriber();
Console.Readkey();
I updated the code like below,
class Program
{
static void Main(string[] args)
{
CreateSubscriber(true);
Console.ReadLine();
CreateSubscriber(false);
Console.ReadLine();
}
static void CreateSubscriber(bool toDo)
{
Subscriber s = new Subscriber(toDo);
s.list.Clear();
}
}
public class Subscriber
{
public List<string> list = new List<string>();
public Subscriber(bool toDo)
{
Console.WriteLine("In Consutructor");
if (toDo)
{
for (long i = 0; i < 5000000; i++)
list.Add(i.ToString());
}
else
{
for (long i = 0; i < 2000000; i++)
list.Add(i.ToString());
}
Console.WriteLine("Out Consutructor");
}
~Subscriber()
{
Console.WriteLine("Destructor Called - Sub");
}
}
output:
and as he expected, on second instance creation of Subscriber
, i could see GC being collected (finalizer being called).
Note that: in else condition of constructor of Subscriber, i m adding less items in list then if condition - to notice if RAM usage of application is being decreased accordingly, Yes it is being decreased too.
there in else condition, i could have left list of string empty (so memory usage will be significantly decreased). But doing so, GC is not being collected. Most likely because of the reason which M.Aroosi has mentioned in question's comment.
in addition to what's said above, the GC will only collect once a generation gets full(or due to an explicit call), and just 1 object created wouldn't trigger it. Yes the object is elligible for finalization, but there's no reason for the GC to collect it.