How finalizer increases the life of an object in C#? - c#

I am reading a book on C# memory management which says:
What’s important to understand is that a finalizer increases the life
of an object. Because the finalization code also has to run, the .NET
Framework keeps a reference to the object in a special finalization
queue. An additional thread runs all the finalizers at a time deemed
appropriate based on the execution context. This delays garbage
collection for types that have a finalizer.
As per my knowledge finalizer would run on garbage collection only, not before it. So, how can it delay its garbage collection?

After going through MSDN links posted by comments and deleted answers, below are the details of the whole process:
When a finalizable object is discovered (by GC) to be dead, its finalizer is put in a queue so that its cleanup actions are executed, but the object itself is promoted to the next generation. Therefore, you have to wait until the next garbage collection that occurs on that generation (which is not necessarily the next garbage collection) to determine whether the object has been reclaimed.
Following is the code to demonstrate the same:
using System;
class Program
{
static void CreateDestructorReferences()
{
for (int i = 0; i < 1000; i++)
_ = new Destructor();
}
static void CreateWithoutDestructorReferences()
{
for (int i = 0; i < 1000; i++)
_ = new WithoutDestructor();
}
static void Main(string[] args)
{
CreateDestructorReferences();
DemoGCProcess("****Objects With Destructors*****");
CreateWithoutDestructorReferences();
DemoGCProcess("****Objects Without Destructors*****");
Console.ReadLine();
}
private static void DemoGCProcess(string text)
{
Console.WriteLine(text);
var memory = GC.GetTotalMemory(false);
GC.Collect(0);
GC.WaitForPendingFinalizers();
var memory1 = GC.GetTotalMemory(false);
Console.WriteLine("Memory freed on first Garbage Collection on Generation 0:" + (memory - memory1));
GC.Collect(1);
var memory2 = GC.GetTotalMemory(false);
Console.WriteLine("Memory freed on second Garbage Collection on Generation 0 and Generation 1:" + (memory1 - memory2));
}
}
class Destructor
{
~Destructor()
{
//Console.WriteLine("Destructor is called");
}
}
class WithoutDestructor
{
}
And here is the output of the above program:

Related

C# Memory leak query - Why GC is not releasing memory of the local scope?

class Program
{
static void Main(string[] args)
{
Console.WriteLine("Before 1st call TestFunc");
TestFunc();
Console.WriteLine("After 1st call TestFunc");
Console.WriteLine("Before 2nd call TestFunc");
TestFunc();
Console.WriteLine("After 2nd call TestFunc");
Console.ReadLine();
}
public static void TestFunc()
{
List<Employee> empList = new List<Employee>();
for (int i = 1; i <= 50000000; i++)
{
Employee obj = new Employee();
obj.Name = "fjasdkljflasdkjflsdjflsjfkldsjfljsflsdjlkfajsd";
obj.ID = "11111111111111112222222222222222222222222222222";
empList.Add(obj);
}
}
}
public class Employee
{
public string Name;
public string ID;
}
I am creating lot of employees only inside a location(local scope), when the control comes back to main function, why DOTNET is not releasing the memory?
Lets say each function call is using 1 GB of memory, at the end of the function main, still the application uses more than 1Gb of memory. Why GC is not collecting after the scope goes off?
This might be a simple question, Any help would be great.
GC doesn't start automatically at the end of scope or function call. According to MS documentation:
Garbage collection occurs when one of the following conditions is true:
•The system has low physical memory. This is detected by either the low memory notification from the OS or low memory indicated by the host.
•The memory that is used by allocated objects on the managed heap surpasses an acceptable threshold. This threshold is continuously adjusted as the process runs.
•The GC.Collect method is called. In almost all cases, you do not have to call this method, because the garbage collector runs continuously. This method is primarily used for unique situations and testing.
Fundamentals of Garbage Collection

why Destructor is not being called for this code

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.
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).
When GC detects that the reference for the instance of Subscriber is lost (out of scope) it will mark this object to be collected on one of the next rounds.
But only GC knows when exactly will this next round be.
Is it because, program is single threaded and that thread is waiting
for user input on Console.Read() ?
No, if we run this code in a separate thread the result will be the same.
However if we change this:
CreateSubscriber();
Console.Read();
To:
CreateSubscriber();
Console.Read();
CreateSubscriber();
Console.Readkey();
We could see that GC will collect the garbage and run the finalizer after Console.Read(). Why?
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.
Let's summarize it a bit:
When we only create an object and there is no reference in code that points
to this obj or its class until program ends - GC allows program to end and
collect the garbage before exiting.
When we create an object and there is some sort of reference to obj
or its class - GC does performs a check and collects the garbage.
There's some complex logic behind how and when does the GC run collect and how and when the lifetime of the obj ends.
A quote from Eric Lippert's answer:
The lifetime may be extended by a variety of mechanisms, including
capturing outer variables by a lambda, iterator blocks, asynchronous
methods, and so on
It's very rare that we need to execute some code on object's destruction. In that specific scenario instead of guessing when the obj will be destroyed we could run GC.Collect explicitly.
More often though we might need to free some managed resources and for that we could use IDisposable interface and using statement that will automatically call Dispose before the control flow leaves the block of code (it'll create a try {} finally {} clause and in finally it will call Dispose for us).
using(myDisposable)
{
...
} // dispose is called here

Some cases when necessary to call GC.Collect manualy

I've read many articles about GC, and about "do no care about objects" paradigm, but i did a test for proove it.
So idea is: i'm creating a lot of large objects stored in local functions, and I suspect that after all tasks are done it will clean the memory itself. But GC didn't. So test code:
class Program
{
static void Main()
{
var allDone = new ManualResetEvent(false);
int completed = 0;
long sum = 0; //just to prevent optimizer to remove cycle etc.
const int count = int.MaxValue/10000000;
for (int i = 0; i < count; i++)
{
ThreadPool.QueueUserWorkItem(delegate
{
unchecked
{
var dumb = new Dumb();
var localSum = 0;
foreach (int x in dumb.Arr)
{
localSum += x;
}
sum += localSum;
}
if (Interlocked.Increment(ref completed) == count)
allDone.Set();
if (completed%(count/100) == 0)
Console.WriteLine("Progress = {0:N2}%", 100.0*completed/count);
});
}
allDone.WaitOne();
Console.WriteLine("Done. Result : {0}", sum);
Console.ReadKey();
GC.Collect();
Console.WriteLine("GC Collected!");
Console.WriteLine("GC CollectionsCount 0 = {0}, 1 = {1}, 2 = {2}", GC.CollectionCount(0), GC.CollectionCount(1),GC.CollectionCount(2));
Console.ReadKey();
}
}
class Dumb
{
public int[] Arr = Enumerable.Range(1,10*1024*1024).ToArray(); // 50MB
}
so in my case app eat ~2GB of RAM, but when I'm clicking on keyboard and launching GC.Collect it free occuped memory up to normal size of 20mb.
I've read that manual calls of GC etc is bad practice, but i cannot avoid it in this case.
In your example there is no need to explicitly call GC.Collect()
If you bring it up in the task manager or Performance Monitor you will see the GC working as it runs. GC is called when needed by the OS (when it is trying to allocate and doesn't have memory it will call GC to free some up).
That being said since your objects ( greater than 85000 bytes) are going onto the large object heap, LOH, you need to watch out for large object heap fragmentation. I've modified your code so show how you can fragment the LOH. Which will give an out of memory exception even though the memory is available, just not contiguous memory. As of .NET 4.5.1 you can set a flag to request that LOH to be compacted.
I modified your code to show an example of this here:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace GCTesting
{
class Program
{
static int fragLOHbyIncrementing = 1000;
static void Main()
{
var allDone = new ManualResetEvent(false);
int completed = 0;
long sum = 0; //just to prevent optimizer to remove cycle etc.
const int count = 2000;
for (int i = 0; i < count; i++)
{
ThreadPool.QueueUserWorkItem(delegate
{
unchecked
{
var dumb = new Dumb( fragLOHbyIncrementing++ );
var localSum = 0;
foreach (int x in dumb.Arr)
{
localSum += x;
}
sum += localSum;
}
if (Interlocked.Increment(ref completed) == count)
allDone.Set();
if (completed % (count / 100) == 0)
Console.WriteLine("Progress = {0:N2}%", 100.0 * completed / count);
});
}
allDone.WaitOne();
Console.WriteLine("Done. Result : {0}", sum);
Console.ReadKey();
GC.Collect();
Console.WriteLine("GC Collected!");
Console.WriteLine("GC CollectionsCount 0 = {0}, 1 = {1}, 2 = {2}", GC.CollectionCount(0), GC.CollectionCount(1), GC.CollectionCount(2));
Console.ReadKey();
}
}
class Dumb
{
public Dumb(int incr)
{
try
{
DumbAllocation(incr);
}
catch (OutOfMemoryException)
{
Console.WriteLine("Out of memory, trying to compact the LOH.");
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
try // try again
{
DumbAllocation(incr);
Console.WriteLine("compacting the LOH worked to free up memory.");
}
catch (OutOfMemoryException)
{
Console.WriteLine("compaction of LOH failed to free memory.");
throw;
}
}
}
private void DumbAllocation(int incr)
{
Arr = Enumerable.Range(1, (10 * 1024 * 1024) + incr).ToArray();
}
public int[] Arr;
}
}
The .NET runtime will garbage collect without your call to the GC. However, the GC methods are exposed so that GC collections can be timed with the user experience (load screens, waiting for downloads, etc).
Use GC methods isn't always a bad idea, but if you need to ask then it likely is. :-)
I've read that manual calls of GC etc is bad practice, but i cannot avoid it in this case.
You can avoid it. Just don't call it. The next time you try to do an allocation, the GC will likely kick in and take care of this for you.
Few things I can think of that may be influencing this, but none for sure :(
One possible effect is that GC doesn't kick in right away... the large objects are on the collection queue - but haven't been cleaned up yet. Specifically calling GC.Collect forces collection right there and that's where you see the difference. Otherwise it would've just happened at some point later.
Second reason i can think of is that GC may collect objects, but not necessarily release memory to OS. Hence you'd continue seeing high memory usage even though it's free internally and available for allocation.
The garbage collection is clever and decide when the time right to collect your objects. This is done by heuristics and you must read about that. The garbage collection makes his job very good. Are the 2GB a problem for yout system or you just wondering about the behaviour?
Whenever you call GC.Collect() don't forget the call GC.WaitingForPendingFinalizer. This avoids unwanted aging of objects with finalizer.

Why does the c# garbage collector not keep trying to free memory until a request can be satisfied?

Consider the code below:
using System;
namespace memoryEater
{
internal class Program
{
private static void Main(string[] args)
{
Console.WriteLine("alloc 1");
var big1 = new BigObject();
Console.WriteLine("alloc 2");
var big2 = new BigObject();
Console.WriteLine("null 1");
big1 = null;
//GC.Collect();
Console.WriteLine("alloc3");
big1 = new BigObject();
Console.WriteLine("done");
Console.Read();
}
}
public class BigObject
{
private const uint OneMeg = 1024 * 1024;
private static int _idCnt;
private readonly int _myId;
private byte[][] _bigArray;
public BigObject()
{
_myId = _idCnt++;
Console.WriteLine("BigObject {0} creating... ", _myId);
_bigArray = new byte[700][];
for (int i = 0; i < 700; i++)
{
_bigArray[i] = new byte[OneMeg];
}
for (int j = 0; j < 700; j++)
{
for (int i = 0; i < OneMeg; i++)
{
_bigArray[j][i] = (byte)i;
}
}
Console.WriteLine("done");
}
~BigObject()
{
Console.WriteLine("BigObject {0} finalised", _myId);
}
}
}
I have a class, BigObject, which creates a 700MiB array in its constructor, and has a finalise method which does nothing other than print to console. In Main, I create two of these objects, free one, and then create a third.
If this is compiled for 32 bit (so as to limit memory to 2 gigs), an out of memory exception is thrown when creating the third BigObject. This is because, when memory is requested for the third time, the request cannot be satisfied and so the garbage collector runs. However, the first BigObject, which is ready to be collected, has a finaliser method so instead of being collected is placed on the finalisation queue and is finalised. The garbage collecter then halts and the exception is thrown. However, if the call to GC.Collect is uncommented, or the finalise method is removed, the code will run fine.
My question is, why does the garbage collector not do everything it can to satisfy the request for memory? If it ran twice (once to finalise and again to free) the above code would work fine. Shouldn't the garbage collector continue to finalise and collect until no more memory can be free'd before throwing the exception, and is there any way to configure it to behave this way (either in code or through Visual Studio)?
Its undeterministic when GC will work and try to reclaim memory.
If you add this line after big1 = null . However you should be carefult about forcing GC to collect. Its not recommended unless you know what you are doing.
GC.Collect();
GC.WaitForPendingFinalizers();
Best Practice for Forcing Garbage Collection in C#
When should I use GC.SuppressFinalize()?
Garbage collection in .NET (generations)
I guess its because the time the finalizer executes during garbage collection is undefined. Resources are not guaranteed to be released at any specific time (unless calling a Close method or a Dispose method.), also the order that finalizers are run is random so you could have a finalizer on another object waiting, while your object waits for that.

GC generation after resurrection?

Suppose an object has a Finalize() method.
When it is first created, a pointer in the finalization queue was added.
The object has no references .
When a garbage collection occurs, it moves the reference from the finalization queue to the f-reachable queue and a thread is started to run the Finalize method (sequentially after other objects' Finalize methods).
So the object now (after resurrection) has only one root which is the pointer from the f-reachable queue.
At this point, does/did the object get promoted to the next generation ?
This is something you can just try. Run this code in the Release build without a debugger attached:
using System;
class Program {
static void Main(string[] args) {
var obj = new Foo();
// There are 3 generations, could have used GC.MaxGeneration
for (int ix = 0; ix < 3; ++ix) {
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.ReadLine();
}
}
class Foo {
int attempt = 0;
~Foo() {
// Don't try to do this over and over again, rough at program exit
if (attempt++ < 3) {
GC.ReRegisterForFinalize(this);
Console.WriteLine(GC.GetGeneration(this));
}
}
}
Output:
1
2
2
So it stays in the generation it got moved to by the collection, moving to the next one on each collection until it hits the last one. Which makes sense in general.
It seems like the answer is yes, this will happen. http://msdn.microsoft.com/en-us/magazine/bb985010.aspx says:
... Two GCs are required to reclaim memory used by objects that require finalization. In reality, more than two collections may be necessary since the objects could get promoted to an older generation.

Categories

Resources