Skip to main content

Memory, Allocations and Session Leaks

Both halves of the memory problem are fixed and measured:

  • Retention. Fifty open/close rounds of the ticket detail form used to retain +76 MB and leave 50 handlers on a static event. They now retain +1.1 MB and leave 0.
  • Allocation. The grid rows are projected into TicketRow once per search instead of being rebuilt on every redraw.

Evidence: MemoryEvidence.md · DisposalChecklist.md · SessionMemoryBudget.md

Run it

cd "LearnWisej-Samples/Performance and Profiling Course/Module 4/WisejPerfLab"
dotnet run -c Release -f net10.0 --urls http://localhost:5804

For the before reading, run Module 3 on 5803 and use the same three buttons there — that folder carries the same measuring instruments and the unfixed form.

What to click

ActionWhat you should see
Memory snapshot Asnapshot A — heap 23.9 MB bus subscribers 0 forms created 0 / disposed 0
Open and close the detail form ×50one Session/OpenCloseDetail rows=50 record
Snapshot B and compareB - A: heap +1.1 MB subscribers 0 forms outstanding 0 — green status
the same three in Module 3B - A: heap +76.0 MB subscribers +50 forms outstanding 0 — red status
Tickets → Search tickets, then Redraw5,000 rows rebound in 1 ms (no query, no new rows) — the redraw allocates nothing
Tickets → double-click a row, then close the form by its Xforms disposed goes up by one and the subscriber count stays where it was — the X and the Close button take the same path
Repeat the ×50 round a second timethe same +1 MB, not +2 MB: a one-off cost does not grow, retention does

The line worth staring at is forms created 50 / disposed 50, which is true before and after. Disposal always ran. What changed is that nothing holds the disposed forms any more.

What changed since Module 3

Diagnostics/MemoryProbe.cs snapshots: heap after a collection, bus subscribers, forms created/disposed
Models/TicketRow.cs new — the row the grid binds, display strings built once
Forms/TicketDetailForm.cs + Dispose(bool): unsubscribe, stop and dispose the timer, null the
DataSource, dispose the image, drop the buffer, dispose components
+ the timer callback checks IsDisposed
+ static Created / DisposedCount counters for the readings
Forms/TicketDetailForm.Designer.cs its Dispose(bool) moved into the .cs (a partial class has one)
Pages/TicketGridPage.cs the search projects TicketRow once and keeps it; Redraw rebinds
MainPage.cs / .Designer.cs + the three snapshot buttons and the memory readout

Module 3 also carries MemoryProbe, the form counters and the three buttons — with the unfixed form — so the before reading can be taken on the code as it stood. That is the first half of this module.

Known simplifications

  • MemoryProbe reads the managed heap only (GC.GetTotalMemory). The Wisej.NET client state, the SQLite page cache and the Kestrel buffers live outside it. For a capacity model, use the process working set as well — see SessionMemoryBudget.md.
  • The counters say that something is retained. Only Paths to Root in a Memory Usage snapshot says why; the doc walks through collecting it.