0

I am trying to find the reasons for the increase of memory usage over time in my app, specially when a user opens and closes a lot of forms. I am doing some tests trying to understand what are the things that I may be doing wrong, and I found a case I can't understand: if I instantiate a form with a static AutoCompleteStringCollection assigned to a ComboBox, the form finalizer is not called until I close the app. If I remove the static keyword, the finalizer is called in the next click and works as expected.

I was able to reproduce it in an empty project using .NET 6: every time I click the button, the memory keeps growing with every instance of the big Bitmap in Form2 and never gets released. Without the static keyword, only one Bitmap is kept in memory, the last one.

My question is: why a static variable not referenced anywhere else is keeping the instance of the Form2 from beeing collected by the GC?

public class Form1 : Form
    {
        public Form1()
        {
            var button = new Button() { Text = "Click Me" };
            button.Click += (s, e) =>
            {
                Form2 dummy = new Form2();
                dummy = null;

                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();

            };
            this.Controls.Add(button);
        }
    }

    public class Form2 : Form
    {
        //Remove static keyword for the finalizer to be called
        private static AutoCompleteStringCollection SearchText = new AutoCompleteStringCollection();
        private Bitmap bigObject = new Bitmap(1000, 10000);

        public Form2()
        {
            var combo = new ComboBox();
            combo.AutoCompleteCustomSource = SearchText;
            this.Controls.Add(combo);
        }

        ~Form2()
        {
            //Not being called until I close the app
        }
    }
César
  • 1
  • 3
  • https://stackoverflow.com/questions/851370/garbage-collection-of-static-members – Roman Ryzhiy Dec 20 '22 at 13:35
  • I understand that the object that won't be collected is the AutoCompleteStringCollection, but I can't see why the Form can't be collected. – César Dec 20 '22 at 14:02
  • SInce you're inheriting from `Form`, it seems that you should override `Dispose` rather than adding a finalizer. Additionally, anything that has `Dispose` (ie: an instance of Form2) should be disposed of. See [Finalizers (C# Programming Guide)](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/finalizers#explicit-release-of-resources) and [Fundamentals of garbage collection](https://learn.microsoft.com/en-us/dotnet/standard/garbage-collection/fundamentals). – Tu deschizi eu inchid Dec 20 '22 at 14:46
  • The following may be of interest: https://www.infoworld.com/article/3614084/how-to-avoid-gc-pressure-in-c-and-net.html – Tu deschizi eu inchid Dec 20 '22 at 14:46
  • I don't use finalizers in my code, it's just for my tests to understand how it works. If I call dummy.Dispose(), it works as expected, memory doesn't grow beyond one Bitmap. Maybe I have to do my tests in another way. – César Dec 20 '22 at 15:10
  • I still don't know the answer to the original question, but it's not very relevant anymore because I found the real source of most of my memory problems: creating instances of BindingSource outside the designer and not disposing them manually either. Replacing all BindingSource with BindingList solved a great part of my memory leaks. The same can happen with ContextMenuStrip and some other components if not created in the designer and not correctly disposed. – César Jan 09 '23 at 10:32

0 Answers0