I'm trying to get to grips with how Windows Forms applications manage memory allocation. I'll give you an illustration of the problem. Take this simple winforms app which is a main form with two buttons: one that opens a form containing some random data, and another button that closes all open forms (except the main form)
Code:
Public Class MainForm
Private WithEvents _timer As System.Timers.Timer = New System.Timers.Timer()
Private Sub UltraButton1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles UltraButton1.Click
Try
Dim frm As Form = New GridForm()
frm.Show()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub _timer_Elapsed(ByVal sender As Object, ByVal e As System.Timers.ElapsedEventArgs) Handles _timer.Elapsed
Dim bytesInUse As Long = GC.GetTotalMemory(False)
Dim mbInUse As Double = Math.Round(bytesInUse / 1024 / 1024, 1)
Dim msg As String = String.Format("Form Count: {0} {1} {2}MB ({3} bytes)", (My.Application.OpenForms.Count - 1).ToString("000"), System.DateTime.Now.ToString("HH:mm:ss fff"), mbInUse.ToString("000"), bytesInUse.ToString("0000000000"))
Debug.Print(msg)
End Sub
Private Sub MainForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
_timer.Interval = 5000
_timer.Enabled = True
End Sub
Private Sub UltraButton2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles UltraButton2.Click
Try
Dim closeForms As List(Of Form) = New List(Of Form)()
For Each frm As Form In My.Application.OpenForms
If frm.Name = "GridForm" Then
closeForms.Add(frm)
End If
Next
For Each frm As Form In closeForms
frm.Close()
frm.Dispose()
Next
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
End Class
Public Class GridForm
Private _randomData As List(Of RandomData) = Nothing
Private Sub GridForm_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Try
_randomData = RandomDataHelper.GetRandomData()
Me.DataGridView1.DataSource = _randomData
Catch ex As Exception
End Try
End Sub
End Class
Friend Class RandomData
Private _guid As String
Private _id As Integer
Public Property MyGuid() As String
Get
Return _guid
End Get
Set(ByVal value As String)
_guid = value
End Set
End Property
Public Property Id() As Integer
Get
Return _id
End Get
Set(ByVal value As Integer)
_id = value
End Set
End Property
End Class
Public Class RandomDataHelper
Private Shared _randomData As List(Of RandomData) = Nothing
Friend Shared Function GetRandomData() As List(Of RandomData)
If _randomData Is Nothing Then
_randomData = New List(Of RandomData)()
For i As Integer = 1 To 100000
_randomData.Add(New RandomData() With {.Id = i, .MyGuid = System.Guid.NewGuid().ToString()})
Next
End If
Dim newList As List(Of RandomData) = New List(Of RandomData)()
For Each rd In _randomData
newList.Add(New RandomData() With {.Id = rd.Id, .MyGuid = rd.MyGuid})
Next
Return newList
End Function
End Class
If you run this and click away opening lots of forms, 50 at a time, 100 at a time, and then closing them all, the memory goes up and down over time but gradually gets higher and higher, never returning to anything like its initial state. Now I assume part of this is the .NET runtime being clever and assuming that as the app needed a lot of memory previously, it will need more again and better to keep some allocated (I am running this on a machine with 4GB RAM).
This is the output
Code:Form Count: 000 08:44:39 755 001MB (0000813092 bytes)
Form Count: 001 08:44:44 728 017MB (0017485136 bytes)
Form Count: 016 08:44:49 716 062MB (0064453476 bytes)
Form Count: 016 08:44:54 705 062MB (0064887652 bytes)
Form Count: 016 08:44:59 694 062MB (0064887652 bytes)
Form Count: 016 08:45:04 682 062MB (0064887652 bytes)
Form Count: 016 08:45:09 671 062MB (0064887652 bytes)
Form Count: 016 08:45:14 660 062MB (0064904036 bytes)
Form Count: 016 08:45:19 648 062MB (0064904036 bytes)
Form Count: 016 08:45:24 637 062MB (0065084260 bytes)
Form Count: 026 08:45:29 626 103MB (0107992376 bytes)
Form Count: 049 08:45:34 614 178MB (0186475920 bytes)
Form Count: 000 08:45:39 603 178MB (0187032976 bytes)
Form Count: 006 08:45:44 594 202MB (0212140692 bytes)
Form Count: 028 08:45:49 699 242MB (0253800456 bytes)
Form Count: 063 08:45:54 585 389MB (0407684876 bytes)
Form Count: 001 08:45:59 581 401MB (0420033088 bytes)
Form Count: 019 08:46:04 576 268MB (0280643940 bytes) -
Form Count: 029 08:46:09 572 313MB (0328246272 bytes)
Form Count: 060 08:46:14 567 202MB (0211634588 bytes) -
Form Count: 000 08:46:19 563 228MB (0238579268 bytes)
Form Count: 021 08:46:24 559 303MB (0317532092 bytes)
Form Count: 050 08:46:29 554 181MB (0189643032 bytes) -
Form Count: 050 08:46:34 550 181MB (0189643032 bytes)
Form Count: 007 08:46:39 592 205MB (0214563092 bytes)
Form Count: 046 08:46:44 541 318MB (0333827408 bytes)
Form Count: 000 08:46:49 537 335MB (0350860716 bytes)
Form Count: 006 08:46:54 532 360MB (0377380588 bytes)
Form Count: 041 08:46:59 528 293MB (0306788368 bytes) -
Form Count: 049 08:47:04 524 329MB (0344529752 bytes)
Form Count: 049 08:47:09 519 329MB (0344529752 bytes)
Form Count: 049 08:47:14 515 329MB (0344529752 bytes)
Form Count: 000 08:47:19 510 329MB (0345054040 bytes)
Form Count: 000 08:47:24 506 329MB (0345070424 bytes)
Form Count: 000 08:47:29 502 329MB (0345095000 bytes)
Form Count: 000 08:47:34 497 329MB (0345095000 bytes)
Form Count: 019 08:47:39 524 404MB (0423631984 bytes)
Form Count: 049 08:47:44 489 166MB (0173639576 bytes) -
Form Count: 049 08:47:49 484 166MB (0173655960 bytes)
Form Count: 000 08:47:54 480 160MB (0168018476 bytes)
Form Count: 000 08:47:59 475 160MB (0168018476 bytes)
Form Count: 013 08:48:04 471 215MB (0225774536 bytes)
Form Count: 050 08:48:09 623 306MB (0320518980 bytes)
Form Count: 090 08:48:14 525 474MB (0496950512 bytes)
Form Count: 099 08:48:19 458 514MB (0538941444 bytes)
Form Count: 099 08:48:24 453 514MB (0538941444 bytes)
Form Count: 099 08:48:29 449 514MB (0538941444 bytes)
Form Count: 099 08:48:34 445 514MB (0538949636 bytes)
Form Count: 099 08:48:39 440 514MB (0538949636 bytes)
Form Count: 099 08:48:44 436 514MB (0539039748 bytes)
Form Count: 000 08:48:49 432 515MB (0540022788 bytes)
Form Count: 012 08:48:54 427 565MB (0592271776 bytes)
Form Count: 049 08:48:59 423 473MB (0496036112 bytes) -
Form Count: 088 08:49:04 418 646MB (0677261468 bytes)
Form Count: 099 08:49:09 414 691MB (0725020668 bytes)
Form Count: 099 08:49:14 410 691MB (0725028860 bytes)
Form Count: 099 08:49:19 405 691MB (0725028860 bytes)
Form Count: 099 08:49:24 401 691MB (0725028860 bytes)
Form Count: 099 08:49:29 396 691MB (0725028860 bytes)
Form Count: 033 08:49:34 392 692MB (0725710488 bytes)
Form Count: 000 08:49:39 388 692MB (0726046360 bytes)
Form Count: 005 08:49:44 383 316MB (0331798056 bytes) -
Form Count: 005 08:49:49 379 318MB (0333050676 bytes)
Form Count: 005 08:49:54 375 318MB (0333050676 bytes)
Form Count: 030 08:49:59 370 101MB (0106193620 bytes) --
Form Count: 063 08:50:04 506 200MB (0209653496 bytes)
Form Count: 065 08:50:09 361 213MB (0223073476 bytes)
Form Count: 065 08:50:14 357 213MB (0223081668 bytes)
Form Count: 000 08:50:19 353 212MB (0222580224 bytes)
Form Count: 004 08:50:24 348 230MB (0241493684 bytes)
Form Count: 005 08:50:29 344 250MB (0262433028 bytes)
Form Count: 005 08:50:34 339 250MB (0262433028 bytes)
Form Count: 005 08:50:39 335 250MB (0262433028 bytes)
Form Count: 005 08:50:44 332 250MB (0262441220 bytes)
Form Count: 005 08:50:49 330 250MB (0262441220 bytes)
Form Count: 005 08:50:54 328 250MB (0262449412 bytes)
Form Count: 035 08:50:59 326 375MB (0393604792 bytes)
Form Count: 070 08:51:04 324 443MB (0463951896 bytes)
Form Count: 098 08:51:09 338 558MB (0585160608 bytes)
Form Count: 000 08:51:14 336 564MB (0591128768 bytes)
Form Count: 003 08:51:19 334 576MB (0604198604 bytes)
Form Count: 003 08:51:24 333 588MB (0616855512 bytes)
Form Count: 000 08:51:29 331 601MB (0629999648 bytes)
Form Count: 020 08:51:34 329 683MB (0716641424 bytes)
Form Count: 005 08:51:39 327 704MB (0738295324 bytes)
Form Count: 013 08:51:44 372 766MB (0802846820 bytes)
Form Count: 048 08:51:49 323 552MB (0578560104 bytes) -
Form Count: 001 08:51:54 322 565MB (0592206272 bytes)
Form Count: 005 08:51:59 320 581MB (0609027444 bytes)
Form Count: 009 08:52:04 318 598MB (0627505656 bytes)
Form Count: 004 08:52:09 316 617MB (0646774500 bytes)
Form Count: 004 08:52:14 314 617MB (0646774500 bytes)
Form Count: 025 08:52:19 313 253MB (0265532620 bytes) -
Form Count: 000 08:52:24 311 254MB (0265835724 bytes)
Form Count: 000 08:52:29 309 254MB (0265835724 bytes)
Form Count: 000 08:52:34 307 254MB (0265876684 bytes)
Form Count: 000 08:52:39 305 254MB (0265876684 bytes)
You can see the memory dropping at intervals (I've marked some of them above) which is good but it still slowly has a minimum that creeps up and up.
This is a very basic illustration of a much bigger production issue with a huge application with 100,000s lines of code and users that keep the app open all day. Over time they open and close a lot of forms and the memory creeps up.
Is there any way to force the allocation down? I know I can force GC but this won't do anything as it's the memory post-GC that is remaining high as far as I can see.
This isn't a problem with references as far as I can see because I have spent some time experimenting with just one form in the main application opening and closing it and there are no references after it is closed and disposed and yet the memory still creeps up in a similar way to that logged above. Also if it was a problem with references, I'm not sure how that would explain my test app above.
Any help or advice on what to use to troubleshoot greatly received.

