EDN Admin
Well-known member
I am running into a memory leak in one of my application where I create controls within the code. While attempting to solve this problem, I created a VERY simple WinForm application with a flowLayoutPanel (dock fill), buttonPanel(dock bottom) and three buttons (added to the buttonPanel):
The code for the UserControl isublic Class MyDemoUserControl
Implements System.IDisposable
Public Sub removeAllHandler()
Dim myEventhandler As New EventHandler(AddressOf myEvent)
RemoveHandler Me.Label1.Click, myEventhandler
End Sub
Private Sub MyDemoUserControl_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
AddHandler Me.Label1.Click, AddressOf myEvent
End Sub
Private Sub myEvent()
MsgBox("You clicked on me")
End Sub
End Class
The code for the main form isublic Class Form1
Private Sub addControlsButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles addControlsButton.Click
Me.FlowLayoutPanel1.Hide()
Eat up some memory
For i As Integer = 0 To 500
Dim newMyDemoUserControl As New MyDemoUserControl
Me.FlowLayoutPanel1.Controls.Add(newMyDemoUserControl)
Next
Me.FlowLayoutPanel1.Show()
End Sub
Private Sub disposeControlsButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles disposeControlsButton.Click
I read that utilizing the suspendLayout would help, but I dont find that it does
Me.SuspendLayout()
Me.FlowLayoutPanel1.Hide()
While Me.FlowLayoutPanel1.Controls.Count > 0
Dim currControl As MyDemoUserControl = Me.FlowLayoutPanel1.Controls(0)
Remove the click handler
currControl.removeAllHandler()
currControl.Dispose()
Me.Controls.Remove(currControl)
If you uncommented the GC.Collect(), it works properly
GC.Collect()
End While
Me.FlowLayoutPanel1.Show()
Me.ResumeLayout()
End Sub
Private Sub gcButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles gcButton.Click
GC.Collect()
End Sub
End Class
Please note, the machine I am developing on has 8GB of RAM, so I used 500 in my For loop to use allocate most of my machines RAM. This may need to be adjusted based on your machines memory.
During my research, I have read how great the GC is at recognizing when to be called in order to release memory allocated to an application. If thats true, then why does this fail? If you click on the addControlsButton, then all userControls are added to the panel. Looking at the task manager, you will see the memory usage goes through the roof and your physical memory percentage rises quickly.
Now, if you click on the removeControlsButton, then all the userControls are removed from the panel. If you are still watching the task manager, you wont see much of anything happen. This isnt concerning, because the GC has not been triggered since the machine doesnt have memory pressure, yet. Its time to add some memory pressure.
Click on the addControlsButton a second time. What, it failed? Why? The code should error out during the For loop (mine only made it to i=61.) You should see an error that states "Parameter not valid" for the userControl. I can only imagine this is due to the machine running out of memory. Why do I say this?
Well, if you click on the GC button, before you click on the addControlsButton for the second time, everything works perfect. Or if you uncomment the GC.Collect() in the While loop, then no error occurs. If you were still paying attention to that wonderful task manager after clicking on the GC button, then youll see the GC was called because the memory drops significantly.
So my questions to everyone are: what am I missing, what am I doing wrong, and why?
View the full article
- addControlsButton - Adds the custom userControl to the flowLayoutPanel
- disposeControlsButton - Loops through the controls in the flowLayoutPanel and disposes of them
- gcButton - calls the garbage collector
The code for the UserControl isublic Class MyDemoUserControl
Implements System.IDisposable
Public Sub removeAllHandler()
Dim myEventhandler As New EventHandler(AddressOf myEvent)
RemoveHandler Me.Label1.Click, myEventhandler
End Sub
Private Sub MyDemoUserControl_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
AddHandler Me.Label1.Click, AddressOf myEvent
End Sub
Private Sub myEvent()
MsgBox("You clicked on me")
End Sub
End Class
The code for the main form isublic Class Form1
Private Sub addControlsButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles addControlsButton.Click
Me.FlowLayoutPanel1.Hide()
Eat up some memory
For i As Integer = 0 To 500
Dim newMyDemoUserControl As New MyDemoUserControl
Me.FlowLayoutPanel1.Controls.Add(newMyDemoUserControl)
Next
Me.FlowLayoutPanel1.Show()
End Sub
Private Sub disposeControlsButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles disposeControlsButton.Click
I read that utilizing the suspendLayout would help, but I dont find that it does
Me.SuspendLayout()
Me.FlowLayoutPanel1.Hide()
While Me.FlowLayoutPanel1.Controls.Count > 0
Dim currControl As MyDemoUserControl = Me.FlowLayoutPanel1.Controls(0)
Remove the click handler
currControl.removeAllHandler()
currControl.Dispose()
Me.Controls.Remove(currControl)
If you uncommented the GC.Collect(), it works properly
GC.Collect()
End While
Me.FlowLayoutPanel1.Show()
Me.ResumeLayout()
End Sub
Private Sub gcButton_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles gcButton.Click
GC.Collect()
End Sub
End Class
Please note, the machine I am developing on has 8GB of RAM, so I used 500 in my For loop to use allocate most of my machines RAM. This may need to be adjusted based on your machines memory.
During my research, I have read how great the GC is at recognizing when to be called in order to release memory allocated to an application. If thats true, then why does this fail? If you click on the addControlsButton, then all userControls are added to the panel. Looking at the task manager, you will see the memory usage goes through the roof and your physical memory percentage rises quickly.
Now, if you click on the removeControlsButton, then all the userControls are removed from the panel. If you are still watching the task manager, you wont see much of anything happen. This isnt concerning, because the GC has not been triggered since the machine doesnt have memory pressure, yet. Its time to add some memory pressure.
Click on the addControlsButton a second time. What, it failed? Why? The code should error out during the For loop (mine only made it to i=61.) You should see an error that states "Parameter not valid" for the userControl. I can only imagine this is due to the machine running out of memory. Why do I say this?
Well, if you click on the GC button, before you click on the addControlsButton for the second time, everything works perfect. Or if you uncomment the GC.Collect() in the While loop, then no error occurs. If you were still paying attention to that wonderful task manager after clicking on the GC button, then youll see the GC was called because the memory drops significantly.
So my questions to everyone are: what am I missing, what am I doing wrong, and why?
View the full article