0

I am trying to chase a memory leak in a multiform C#.Net application, on Windows 7, VS 2008.

I found this SO post, which indicates that the second form's finalizer should be automatically called this.Dispose() doesn't release memory used by Form after closing it. However, it isn't working for me.

Each time a pop up the second form (which contains a large string, and a PictureBox), the amount of memory the app uses increases, even when I force a GC. Interestingly, the String's finalizer IS being called. I've add a log to both form's Dispose methods; Form2's dispose is being called.

The code is Form1 (main form)

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

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

        ~Form1()
        {
            System.Diagnostics.Debugger.Log(0, "", "Form1.Destructor has been called.\n");
        }

        private void GC_Click(object sender, EventArgs e)
        {
            System.GC.Collect();
            System.GC.WaitForPendingFinalizers();
            System.GC.Collect();
            System.GC.WaitForPendingFinalizers();
        }

        private void OpenForm_Click(object sender, EventArgs e)
        {
            Form secondForm = null;

            if (secondForm == null)
            {
                secondForm = new Form2();
                secondForm.Show();
            }

            secondForm = null;
        }

        private void UpdateMemory_Click(object sender, EventArgs e)
        {
            long memory =  System.GC.GetTotalMemory(false);
            this.MemoryUsage.Text = "Memory usage: " +
                String.Format("{0:n}", memory) +
                " bytes";
        }

        private void AllocateString_Click(object sender, EventArgs e)
        {
//            StringWrapper wrapper = new StringWrapper();
        }
    }

    public class StringWrapper
    {
        String str = new String('*', 1024 * 1024);

        ~StringWrapper()
        {
            System.Diagnostics.Debugger.Log(0, "", "StringWrapper finalizer has been called.\n");
        }
    }
}

and Form2 is

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

namespace MemoryLeakTest
{
    public partial class Form2 : Form
    {
        StringWrapper wrapper = new StringWrapper();
        public Form2()
        {
            InitializeComponent();
            this.pictureBox1.Image = Image.FromFile("C:/Windows/Web/Wallpaper/Windows/img0.jpg");
        }

        ~Form2()
        {
            System.Diagnostics.Debugger.Log(0, "", "Form2.Finalizer has been called.\n");
        }
    }
}

I haven't included the designer generated code, but can add it if need be

Community
  • 1
  • 1
CSM
  • 1,232
  • 1
  • 8
  • 12
  • Are you sure you actually have a memory leak (as opposed to memory which has not yet been released by the garbage collector)? Did you try using `long memory = System.GC.GetTotalMemory(true);` ? – sgmoore Feb 11 '17 at 12:51
  • I'm not convinced enough I'll get a repro to try it. The code however does show a classic problem in Winforms code. It uses very little generational memory and does not dispose the bitmap. Bitmaps are always troublemakers, they use a large amount of unmanaged memory but the Bitmap class object is very small. PictureBox.Dispose() does *not* also dispose the Image, it cannot assume that it owns the object. So you have to do it yourself. Post a screenshot of the memory profiler you use to get more leads. – Hans Passant Feb 11 '17 at 12:57
  • Try to declare secondForm as a variable of Form1 and remove the instructions "secondForm = null;" – Graffito Feb 11 '17 at 13:04

0 Answers0