views:

69

answers:

5

http://www.devx.com/tips/Tip/5573

The above post states that there will not be memory leak during object construction.

How about the code below? Will this cause any memory leaks? I see that the memory usage for the sample application increases slowly (in task manager) even after forcing the GC collection.

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            Thread t = new Thread(Test);
            t.Start();
        }

        bool b = true;
        long i = 0;

        void Test(object x)
        {
            while (b)
            {
                try
                {
                    lock (this)
                    {
                        i++;
                    }
                    TestClass tt = new TestClass();
                }
                catch(Exception e)
                { 
                }

                GC.Collect();
                GC.Collect();
                GC.Collect();
                GC.Collect();
                GC.Collect();
                GC.Collect();
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            lock (this)
            {
                Text = i.ToString();
            }
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            lock (this)
            {
                b = checkBox1.Checked;
            }
        }
    }

    public class TestClass : UserControl
    {
        Button c = new Button();
        public TestClass()
        {
            for (int i = 0; i < 1000; i++)
            {
                c.Paint += new PaintEventHandler(TestClass_Paint);
                c.ParentChanged += new EventHandler(TestClass_ParentChanged);
                c.PreviewKeyDown += new PreviewKeyDownEventHandler(TestClass_PreviewKeyDown);
            }

            // Throw the exception explicitly.
            throw new Exception("Whaaaa?");
        }

        void TestClass_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {

        }

        void TestClass_ParentChanged(object sender, EventArgs e)
        {

        }

        void TestClass_Paint(object sender, PaintEventArgs e)
        {

        }
    }
}
A: 

You are creating a huge bunch of test objects, but even forcing the the GC to collect wont free up all of the memory the objects are using. GC will try and free up as much as possible but not all of it, and considering you are constantly running that loop making lots of garbage, its likely that the memory usage will increase.

The memory that is used by allocated objects on the managed heap surpasses an acceptable threshold. This means that a threshold of acceptable memory usage has been exceeded on the managed heap. This threshold is continuously adjusted as the process runs.

kyndigs
A: 

I think you are making the wrong conclusion by saying your are seeing the memory usage slightly grow.... The GC is undeterministic, so you never exactly know when the GC is collecting garbage.... even when explicitly collecting with GC.Collect there is no guarantee that after that call the memory is deallocated...

You have to stop the creation of the instances and have to wait a while (say approx. 10 minutes) and see what happens next....

Patrick Peters
+2  A: 

Calling GC.Collect by hand is almost invariably a bad thing, and I'd especially add that if you're calling it six times in a row then you're ruining any chance the GC had at managing your application's memory sensibly.

GC.Collect() won't clear up items that are still reference; in Debug mode this may include your variable tt. Calling GC.Collect in that scenario will just push the referenced object into GC generation 1, which means it's not going to be (automatically) collected for much longer.

Besides this, monitoring memory usage through task manager is never reliable. I wouldn't trust task manager to indicate a memory leak if it's only creeping up slowly.

Anyway, my answer to your question would be: no, you're not leaking memory. I think you might be seeing memory use increase (which is different to a memory leak!). The memory could be reclaimed, but your poor use of the garbage collector is probably delaying that process, and so your memory use is higher than it might otherwise be.

Dan Puzey
That is not the answer. Everyone knows calling GC is bad the question is why it keeps going up. IT IS LEAKING MEMORY!!
Aliostad
It is NOT leaking memory! If you stop calling GC manually and give the system a chance to reclaim memory, *it will go back down*. The memory is artificially high and creeping up because the code is *preventing* it from being cleaned up. As you pointed out in your own answer, the GC is less aggressive in Debug mode, and so the memory won't be released so quickly. This is NOT the same as a leak.
Dan Puzey
Can I make use of the Process class to see if there is a memory leak? It provides WorkingSet, PagedMemorySize and VirtualMemorySize and others...
dattebayo
A: 

Your code will not cause any memory leak. During the first seconds, you will see the memory used by the program increase because the program allocates from the system the memory to store all the objects. But after that the memory usage is stable.

Beware that the TaskManager "Memory Usage" value records the "working set", that is the amount of physical memory used by the program, which fluctuates according to the memory usage of all the other programs and file cache. You have to monitor the "private bytes" of the program, which is the amount of memory the program allocates to store data, which may be mapped to physical memory or on disk in the page file. This is the "VM Size" in Task Manager. The best would be to monitor the .Net performance counters, that will show you how much memory your program has in each GC generation. One easy way to see them is to use Process Explorer. You will see that the loop in the TestClass constructor may fill the generation 0 heap and some objects will pass to generation 1, until the next GC. But Generation 2 will stay empty, and that is where you would find leaking objects if there where some.

plodoc
A: 

In this particular example code, I don't think you have a memory leak, but if your control were to e.g. subscribe to events from the parent control within its constructor, that would generate a memory leak if such events were not unsubscribed.

In vb.net, one can 'smuggle' a copy of an object under construction to the method that's invoking the constructor; it is thus possible to ensure that Dispose will get called on an object that throws in constructors or initializers (the Dispose method must be prepared to deal with the possibility of an incompletely-initialized object, and Dispose should undo any event subscriptions and Dispose any 'owned' iDisposable objects). In C#, I don't know any way to smuggle a copy of an object under construction except by using a ThreadStatic variable or some other kludge, nor do I know of any way for a base class to ensure that a derived class's constructor won't throw an exception.

supercat