GC not shutting down with UserControl?

I have a CF application that UserControls flows over time. This took some time, but I narrowed it down and even reproduced the behavior in the full structure (3.5). Since the behavior exists in both cases, I don’t want to call it a mistake, but I’m sure I don’t understand why this is happening, and I hope that someone can shed light on it.

Therefore, I am creating a simple WinForms application with a form and a button. Clicking the button alternates between creating a new UserControl and Disposing this control. Very simple.

public partial class Form1 : Form { public Form1() { InitializeComponent(); } UserControl1 m_ctl; private void button1_Click(object sender, EventArgs e) { if (m_ctl == null) { m_ctl = new UserControl1(); m_ctl.Visible = true; this.Controls.Add(m_ctl); } else { this.Controls.Remove(m_ctl); m_ctl.Dispose(); m_ctl = null; GC.Collect(); } } } 

And here is the UserControl. It simply keeps track of the number of live (i.e., incomplete) instances. He has nothing but a single mark so that I can visually confirm it in the form.

 public partial class UserControl1 : UserControl { private static int m_instanceCount = 0; public UserControl1() { var c = Interlocked.Increment(ref m_instanceCount); Debug.WriteLine("Instances: " + c.ToString()); InitializeComponent(); } ~UserControl1() { var c = Interlocked.Decrement(ref m_instanceCount); Debug.WriteLine("Instances: " + c.ToString()); } } 

The strange thing here is that the number of instances is growing endlessly. In the end, my device runs out of memory. I suspect I'm on a PC too, I'm just not inclined to press a button for next year.

Now, if I change the default UserControl method, the constructor-created Dispose method like this is just adding a call to ReRegisterForFinalize:

 protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); if (disposing) { GC.ReRegisterForFinalize(this); } } 

Then it behaves exactly as expected, completing the instances at the time of collection (with manual or automatic).

So why is this happening? Obviously, the base calls SuppressFinalize, but why exactly is this happening, and why is this default behavior in Odin's name?

+6
source share
1 answer

So why is this happening? Obviously, the base calls SuppressFinalize, but why exactly is this happening, and why is this default behavior in Odin's name?

This is the default behavior for classes (correctly) that implement IDisposable . When you call IDisposable.Dispose , the proposed default behavior is to suppress finalization, since the main reason for termination is to clear resources that have never been deleted. This is because finalizing is an expensive operation — you don’t want the objects to be unnecessarily completed, and if Dispose was called, it is believed that you have already cleared your unmanaged resources. Any managed memory will be processed independently.

You must override Dispose and decrement within the Dispose override.

This behavior is explained in the documentation for IDisposable . A sample method for calling the Dispose method is (from the reference documentation):

 public void Dispose() { Dispose(true); // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SupressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time GC.SuppressFinalize(this); } 
+4
source

Source: https://habr.com/ru/post/948172/


All Articles