0

I know how to use IDisposable interface in my class. But, I don't know how to free memory which is allocated by my class variables? for example in my class i have created a List<Bitmap> with the 500 bitmap images in the list and I am clearing this list in the dispose method. But, it shows same result when i fetch the total memory using GC.GetTotalMemory().

Here, I have created a sample class implemented by IDisposable interface.

public class disposable : IDisposable
{
    List<Bitmap> list = new List<Bitmap>();
    public disposable()
    {
        for (int i=0; i< 500; i++)
        {
            Bitmap bmp = new Bitmap(1024,768);                    
            using (Graphics g = Graphics.FromImage(bmp))
            {
                g.FillRectangle(Brushes.Red, new Rectangle(0,0,1024,768));
            }
            list.Add(bmp);                    
        }
    }


    public void Dispose()
    {
        list.Clear();
        list = null;
    }
}

Then I have executed following code

static void Main(string[] args)
{

    long l1 = GC.GetTotalMemory(true);
    using (disposable d = new disposable())
    {
        //nothing to do
    }

    long l2 = GC.GetTotalMemory(false);

    Console.WriteLine(l1.ToString());
    Console.WriteLine(l2.ToString());

    Console.ReadKey();
}

The output says.

181764 //bytes before creating object of disposable class
222724 //bytes after creating object of disposable class

Then I have tried without using keyword with simple declaration.

Dim d As New disposable();

But, it is giving me same result.

So, my question is why memory don't get free which is allocated by those bitmap images even I dispose the object. It doesn't make any difference. I am clearing all items from the list and also assigning null value to list object.

I have also tried same thing with declaring byte array instead of list<Bitmap> and SqlConnection object.

But, it was not showing any difference in total memory allocated between both methods(simple declaration and using statement).

long l1 = GC.GetTotalMemory(true);

//SqlConnection cnn = new SqlConnection("");    //1st Method

using (SqlConnection cnn = new SqlConnection("")) //2nd Method
{
    //nothing to do
}

long l2 = GC.GetTotalMemory(false);

Console.WriteLine(l1.ToString());
Console.WriteLine(l2.ToString());

I can't understand why it does not clear the memory which is allocated by both managed and un-managed objects?

Shell
  • 6,818
  • 11
  • 39
  • 70
  • There are two patterns for IDisposable, described here: https://msdn.microsoft.com/en-us/library/system.idisposable%28v=vs.110%29.aspx I suggest you that, until you are very good, you follow them. – xanatos Apr 25 '15 at 05:57

3 Answers3

4

The garbage collector does not run all the time but only when necessary. In my tests sometimes it may run only once in 5 minutes. You can force the garbage collector to collect with GC.Collect(); and GC.WaitForPendingFinalizers();

Z .
  • 12,657
  • 1
  • 31
  • 56
  • So does that mean assigning `null` value to the object reference does not clear the memory directly from the heap storage? and I am just removing the address from the object reference? – Shell Apr 25 '15 at 03:21
  • no, it does not clear the memory immediately. it will eventually get cleaned but not right away. this is why Dispose exists as opposite of the finalizer. the Dispose method will execute when the object goes out of scope, i.e. with the `using` clause, so you can clean us resources that you are using. the finalizer will execute much later, when the GC runs. – Z . Apr 25 '15 at 03:26
  • Thanks Zdravko Danev, your answer is very helpful for me. I learnt something new about the IDisposable. Before, this answer the definition of IDisposable interface was different for me. – Shell Apr 25 '15 at 04:53
  • @Shell: Systems which eagerly free memory do so as a way of telling the memory manager "Here's some memory you can use the next time you want some--you keep track of it". The memory manager could be a lot simpler if memory never got freed until the precise moment it could be allocated for something else, but of course things seldom work out that way. One of the ideas behind GC is that memory should be reclaimed sometime between the point where it's no longer needed and the point where it could be used for something else, but it doesn't matter when within that interval reclamation happens. – supercat May 01 '15 at 15:46
3

Clearing your list or assigning an object the value of null does not release memory in the same way a C/C++ free() call would. It simply indicates to the runtime that you are finished with the object. From time to time the GC (garbage collector) will run. It will look though the objects and trace each objects ownership tree. If it finds the object has no owner it will free the memory (return it to the managed heap). It's actually a bit more complicated than that and there are multiple "generations" based on size and usage, but the idea is the same. Before the object is "freed" the object finalizer (if one is defined) is run. The idea is that the system doesn't spend a lot of time dealing with memory management ... unless it has to.

Dweeberly
  • 4,668
  • 2
  • 22
  • 41
  • Thanks @Dweeberly, you have also cleared my doubt in some way. I was testing my code in a wrong way. Can you help me in one more point. Can you give me a link for article or post which can help me to decide when should I call GC.Collect() method? – Shell Apr 25 '15 at 04:57
  • I'm not sure there is one. For the most part you don't want to call GC.Collect manually. As always there are exceptions to that rule. You might want to look at this SO question http://stackoverflow.com/questions/478167/when-is-it-acceptable-to-call-gc-collect and this MS blog http://blogs.msdn.com/b/ricom/archive/2004/11/29/271829.aspx – Dweeberly Apr 26 '15 at 00:36
0

The garbage collectors in both .NET and Java are designed on the premises that

  1. The purpose of freeing memory is to allow it to be used to satisfy future allocation requests;
  2. Waiting longer before doing a garbage collection will, up to a certain point, generally increase the amount of memory it can free more than it increases the computational cost (thus reducing the average-per-megabyte net cost of allocation and reclamation).

Consequently, .NET nor Java don't seek to reclaim memory when the last remaining reference is destroyed, but rather when there would be an opportunity to put reclaimed memory to use. They won't generally wait until all memory is filled in order to do this (because of caching and other factors, it's often better for the GC to expend most of its efforts repeatedly reclaiming a small pool) but in the absence of any immediate need for reclaimed memory there's generally little benefit to find out what memory would be available.

supercat
  • 77,689
  • 9
  • 166
  • 211