.NET Debugging Demos Lab 3: Memory - Walkthrough
This is the walkthrough of Lab 3 in the Buggy Bits series
I have removed some parts of the original text to make the post a little bit shorter, but as usual, my comments are inline
Examine the counters
-
What are the values for GC Heap Size (MB), and Working Set (MB) after the test?
[System.Runtime] % Time in GC since last GC (%) 1 Allocation Rate (B / 1 sec) 8 168 CPU Usage (%) 0 Exception Count (Count / 1 sec) 0 GC Heap Size (MB) 650 Gen 0 GC Count (Count / 1 sec) 0 Gen 0 Size (B) 192 Gen 1 GC Count (Count / 1 sec) 0 Gen 1 Size (B) 473 345 648 Gen 2 GC Count (Count / 1 sec) 0 Gen 2 Size (B) 213 882 288 LOH Size (B) 245 224 Monitor Lock Contention Count (Count / 1 sec) 0 Number of Active Timers 0 Number of Assemblies Loaded 140 ThreadPool Completed Work Item Count (Count / 1 sec) 0 ThreadPool Queue Length 0 ThreadPool Thread Count 9 Working Set (MB) 787
- Can you tell from this if it appears to be a native leak or a .net memory leak, or a leak on hte loader heap?
Most of the memory is used by the GC Heap - so this is definitely a managed memory problem
- Does the memory go down if you wait some time or stay the same?
Seems to stay pretty stable
- Looking at the counters for GC counts - do any collections occur after the test is finished? why or why not?
I have mentioned this a number of times before (like in the GC pop-quiz) but it’s worth mentioning again, garbage collections only occurs on allocations or when you manually call GC.Collect(). After our stress run is done we don’t do neither so no-one will collect the memory until its needed.
Examine task manager
- Add the column
Working Set
in Task Manager -
Compare the values for
Memory
andWorking Set
to the values for Working Set in the performance counters.Note: since they are given in K in task manager you need to multiply these values by 1024 if you want to compare them
- What does Memory show?
Around 760 MB
- What does Working set show?
Around 720 MB
Here is a description of the different columns in task manager and what they actually show. Note however that most of them show reserved memory - and as we reserve memory in blocks, it doesn’t necessarily equate to the amount of memory used by individual objects, but will give a good indication of how memory grows
- Working set (memory): The amount of physical memory the process is currently using.
- Peak working set (memory): The maximum amount of physical memory the process has used.
- Working set delta (memory): The change in working set memory from the last refresh of the data here.
- Memory (active private working set): The amount of physical memory used by the process that can’t be used by other processes. Processes frequently cache some data to make better use of your RAM, but can quickly give up that memory space if another process needs it. This column excludes data from suspended UWP processes.
- Memory (private working set): The amount of physical memory used by the process that can’t be used by other processes. This column does not exclude data from suspended UWP processes.
- Memory (shared working set): The amount of physical memory used by the process that can be used by other processes when necessary.
- Commit size: The amount of virtual memory Windows is reserving for the process.
Some things to note about the performance counters
- Gen 0 heap size shows the budget for Gen 0, not the size of the objects in Gen 0
- Gen 0, 1, 2 and LOH counters are updated after each collection, not after each allocation
- Objects over 85000 bytes in size end up on the Large Object Heap
- The working set consists of the pages loaded in RAM that have been recently touched by the process, it really doesn’t have anything to do with the private bytes of the process (which is what we usually look at when we look at the memory usage of the process), it may be more and it may be less than private bytes. For win forms apps (non-services) there is usually a big difference in the working set when you minimize the apps since the pages are then trimmed from the working set. For services (like ASP.NET that usually doesnt happen so private bytes and working sets tend to stay in the same range)
Get a memory dump
- Capture a dump with
procdump -ma iisexpress.exe
ordotnet-dump -n iisexpress.exe
Analyze the memory dump
- Open the dump file in windbg
- Set up the symbol path and load sos (see the setup instructions for more info)
- How big is the dump? Look at the size in the file explorer.
1 048 233 kb
- How does this compare to the different counters?
It is usually pretty similar to the working set
Examine the .NET GC Heaps
-
Run
!eeheap -gc
to examine the size of the .NET GC heaps0:000> !eeheap -gc Number of GC Heaps: 8 ------------------------------ Heap 0 (00000122DDF3FDE0) generation 0 starts at 0x00000122E3337880 generation 1 starts at 0x00000122DF8A4C58 generation 2 starts at 0x00000122DE3A1000 ephemeral segment allocation context: none segment begin allocated size 00000122DE3A0000 00000122DE3A1000 00000122E3525FE8 0x5184fe8(85479400) Large object heap starts at 0x00000126DE3A1000 segment begin allocated size 00000126DE3A0000 00000126DE3A1000 00000126DE3B5848 0x14848(84040) Heap Size: Size: 0x5199830 (85563440) bytes. ------------------------------ Heap 1 (00000122DDF66FE0) generation 0 starts at 0x000001236344B940 generation 1 starts at 0x000001235FC80CF8 generation 2 starts at 0x000001235E3A1000 ephemeral segment allocation context: none segment begin allocated size 000001235E3A0000 000001235E3A1000 0000012363765FE8 0x53c4fe8(87838696) Large object heap starts at 0x00000126EE3A1000 segment begin allocated size 00000126EE3A0000 00000126EE3A1000 00000126EE3A1018 0x18(24) Heap Size: Size: 0x53c5000 (87838720) bytes. ------------------------------ Heap 2 (00000122DDF6A1D0) generation 0 starts at 0x00000123E362A040 generation 1 starts at 0x00000123DFF99500 generation 2 starts at 0x00000123DE3A1000 ephemeral segment allocation context: none segment begin allocated size 00000123DE3A0000 00000123DE3A1000 00000123E37841D8 0x53e31d8(87962072) Large object heap starts at 0x00000126FE3A1000 segment begin allocated size 00000126FE3A0000 00000126FE3A1000 00000126FE3A2050 0x1050(4176) Heap Size: Size: 0x53e4228 (87966248) bytes. ------------------------------ Heap 3 (00000122DDF6D3C0) generation 0 starts at 0x0000012463A9E508 generation 1 starts at 0x00000124600B5790 generation 2 starts at 0x000001245E3A1000 ephemeral segment allocation context: none segment begin allocated size 000001245E3A0000 000001245E3A1000 0000012463DB5FE8 0x5a14fe8(94457832) Large object heap starts at 0x000001270E3A1000 segment begin allocated size 000001270E3A0000 000001270E3A1000 000001270E3C1050 0x20050(131152) Heap Size: Size: 0x5a35038 (94588984) bytes. ------------------------------ Heap 4 (00000122DDF705B0) generation 0 starts at 0x00000124E3467500 generation 1 starts at 0x00000124DFC5D720 generation 2 starts at 0x00000124DE3A1000 ephemeral segment allocation context: none segment begin allocated size 00000124DE3A0000 00000124DE3A1000 00000124E35B19F8 0x52109f8(86051320) Large object heap starts at 0x000001271E3A1000 segment begin allocated size 000001271E3A0000 000001271E3A1000 000001271E3A74A0 0x64a0(25760) Heap Size: Size: 0x5216e98 (86077080) bytes. ------------------------------ Heap 5 (00000122DDF7B7B0) generation 0 starts at 0x00000125637F4A50 generation 1 starts at 0x000001255FDBE130 generation 2 starts at 0x000001255E3A1000 ephemeral segment allocation context: none segment begin allocated size 000001255E3A0000 000001255E3A1000 0000012563B41FE8 0x57a0fe8(91885544) Large object heap starts at 0x000001272E3A1000 segment begin allocated size 000001272E3A0000 000001272E3A1000 000001272E3A1018 0x18(24) Heap Size: Size: 0x57a1000 (91885568) bytes. ------------------------------ Heap 6 (00000122DDF809B0) generation 0 starts at 0x00000125E302A0A0 generation 1 starts at 0x00000125DFB19700 generation 2 starts at 0x00000125DE3A1000 ephemeral segment allocation context: none segment begin allocated size 00000125DE3A0000 00000125DE3A1000 00000125E3291FE8 0x4ef0fe8(82776040) Large object heap starts at 0x000001273E3A1000 segment begin allocated size 000001273E3A0000 000001273E3A1000 000001273E3A1018 0x18(24) Heap Size: Size: 0x4ef1000 (82776064) bytes. ------------------------------ Heap 7 (00000122DDF85EE0) generation 0 starts at 0x000001266379AD28 generation 1 starts at 0x000001265FE58080 generation 2 starts at 0x000001265E3A1000 ephemeral segment allocation context: none segment begin allocated size 000001265E3A0000 000001265E3A1000 000001266398C2E0 0x55eb2e0(90092256) Large object heap starts at 0x000001274E3A1000 segment begin allocated size 000001274E3A0000 000001274E3A1000 000001274E3A1018 0x18(24) Heap Size: Size: 0x55eb2f8 (90092280) bytes. ------------------------------ GC Heap Size: Size: 0x2a20bc20 (706788384) bytes.
- How many heaps do you have? Why?
I have 8 heaps - one for each processor core. See the GC Pop Quiz for more info
- How much memory is stored on the .net GC heaps? Compare to
GC Heap Size
690 MB - so exactly as advertised
0:000> ?0n706788384/0n1024 Evaluate expression: 690223 = 00000000`000a882f
- How much memory is on the large object heap? Hint: add up the sizes for all the Large Object heap segments. Compare to
LOH Size
in the perfmon counters.~240 MB
- How many heaps do you have? Why?
-
Run
!dumpheap -stat
to dump out all .net objects in a statistical fashion (Note: you can run!help DumpHeap
to get help for the!dumpheap
command or check out this post or this post)0:000> !dumpheap -stat Statistics: MT Count TotalSize Class Name ... 00007ffcf7a92aa8 2537 147424 System.SByte[] 00007ffcf84d5788 7224 173376 System.Diagnostics.Tracing.PollingPayloadType 00007ffcf7af0db0 1748 181792 System.Reflection.RuntimeMethodInfo 00007ffcf79d6618 426 187896 System.Object[] 00007ffcf8253bf8 3081 236120 System.Collections.Generic.KeyValuePair`2[[System.String, System.Private.CoreLib],[System.Object, System.Private.CoreLib]][] 00007ffcf79db1f0 11439 274536 System.Int32 00007ffcf84dd788 4214 370832 System.Diagnostics.Tracing.IncrementingCounterPayload 00007ffcf84d5cf0 7224 751296 System.Diagnostics.Tracing.CounterPayload 00007ffcf7a92360 517 801427 System.Byte[] 00007ffcf81d8378 32008 1024256 BuggyBits.Models.Link 00007ffcf7a91e18 22676 1258732 System.String 00007ffcf7c6e470 32368 1553664 System.Text.StringBuilder 00000122ddea5ce0 50801 54243104 Free 00007ffcf7ad3058 32428 641275786 System.Char[] Total 282813 objects
- Looking at the 5-10 object types that use most memory, what do you think is leaking?
Most of the memory is used up by
Strings
andChar[]
which is not that unusual to see given how common strings are in any type of app, but ~650 MB definitely seems a bit weird, and 32008Links
(whatever they may be) also sound fishy, especially since there is about the same amount ofStringBuilder
objects present in the dump. - What “size” does the size column show? I.e. what is included in this “size”? Hint: see the post !dumpheap -stat explained for more info.
If we dump out a Link object with !do we will see that a Link object has a pointer to a StringBuilder (url) and a pointer to a string (name). The size of the link object is shown as 32 bytes which basically just includes the pointers and some overhead (method table etc.)
0:000> !DumpObj /d 000001266397e508 Name: BuggyBits.Models.Link MethodTable: 00007ffcf81d8378 EEClass: 00007ffcf81c8c60 Size: 32(0x20) bytes File: C:\Tess\source\BuggyBits\src\BuggyBits\bin\Debug\netcoreapp3.1\BuggyBits.dll Fields: MT Field Offset Type VT Attr Value Name 00007ffcf7c6e470 4000005 8 ...ext.StringBuilder 0 instance 000001266397e528 <URL>k__BackingField 00007ffcf7a91e18 4000006 10 System.String 0 instance 000001255e404dc8 <Title>k__BackingField
If on the other hand you run !objsize on it, you will see that the size shows up as 20168 bytes. This is the size including the member variables, i.e. the size of the Link object and everything it references.
0:000> !objsize 000001266397e508 sizeof(000001266397E508) = 20168 (0x4ec8) bytes (BuggyBits.Models.Link)
What you see in
!dumpheap
is the 32 bytes per link. It wouldn’t be feasible to include the size of the member variables for a few different reasons.- it would take a long time to calculate
- some objects (say A and B) may both point to object C, if you did
!objsize
on A and B they would both include the size for C so the size column would be very confusing since it wouldn’t be exclusive. - in a case like this one with the Link the size for
!objsize
feels like the correct size since a Link contains a url and a name. However if you look at a web control which has a member var _parent and you run!objsize
on the web control, this will include the size of the parent which is not necessarily as obvious.
- Looking at the 5-10 object types that use most memory, what do you think is leaking?
-
Dump out stats for various size char[] to find out if there is a pattern (this is a bit of trial and error so you have to try a few different sizes to figure out where the bulk of the strings are.
Get the method table (MT) for System.Char[] (first column in !dumpheap -stat)
!dumpheap -mt <char[] MT> -min 0n85000 -stat !dumpheap -mt <char[] MT> -min 0n10000 -stat !dumpheap -mt <char[] MT> -min 0n20000 -stat !dumpheap -mt <char[] MT> -min 0n30000 -stat !dumpheap -mt <char[] MT> -min 0n25000 -stat
- In what range (between what sizes) do most of the char[] exist?
Between 20000 and 25000 bytes
- In what range (between what sizes) do most of the char[] exist?
-
Dump out the strings in that range
!dumpheap -mt <char[] MT> -min 0n20000 -max 0n25000
In this case most of them will even be the exact same size which is a clue to what is going on
0:000> !dumpheap -mt 00007ffcf7ad3058 -min 0n20000 Address MT Size 00000122de50f630 00007ffcf7ad3058 20024 00000122de514508 00007ffcf7ad3058 20024 00000122de5193a8 00007ffcf7ad3058 20024 00000122de51e248 00007ffcf7ad3058 20024 00000122de5230e8 00007ffcf7ad3058 20024 00000122de527fe0 00007ffcf7ad3058 20024 00000122de52ce80 00007ffcf7ad3058 20024 00000122de531d20 00007ffcf7ad3058 20024 00000122de540b40 00007ffcf7ad3058 20024 00000122de545a18 00007ffcf7ad3058 20024 00000122de54a8b8 00007ffcf7ad3058 20024 ...
-
Dump out a few of them to find out what they contain
!do <address of char[] - first column in the !dumpheap -mt output>
0:000> !DumpObj /d 0000012663974830 Name: System.Char[] MethodTable: 00007ffcf7ad3058 EEClass: 00007ffcf7ad2fd8 Size: 20024(0x4e38) bytes Array: Rank 1, Number of elements 10000, Type Char (Print Array) Content: http://blogs.msdn.com/tom....................................................................................................... Fields: None
- What do these char[] contain?
Link URLs
- What do these char[] contain?
-
Pick a few and find out where they are rooted (i.e. why they can’t be collected) Note: You may want to try a couple different ones.
!gcroot <address of char[]>
0:000> !gcroot 00000122de50f630 Finalizer Queue: 00000122DE50F5C8 -> 00000122DE50F5C8 BuggyBits.Models.Link -> 00000122DE50F5E8 System.Text.StringBuilder -> 00000122DE50F630 System.Char[] Found 1 unique roots (run '!gcroot -all' to see all roots).
- Where are they rooted? Why?
The
Char[]
is a member of aStringBuilder
which in turn is a member of aLink
object, which is rooted in the Finalizzer Queue so it is ready for finalization, just waiting for the finalizer to call its destructor so it can be garbage collected.
- Where are they rooted? Why?
Examine the finalizer queue and the finalizer thread
-
Look at the finalizer queue
!finalizequeue
```cmd 0:000> !finalizequeue SyncBlocks to be cleaned up: 0 Free-Threaded Interfaces to be released: 0 MTA Interfaces to be released: 0 STA Interfaces to be released: 0 ---------------------------------- ------------------------------ Heap 0 generation 0 has 80 finalizable objects (000001276A6A8228->000001276A6A84A8) generation 1 has 5 finalizable objects (000001276A6A8200->000001276A6A8228) generation 2 has 34 finalizable objects (000001276A6A80F0->000001276A6A8200) Ready for finalization 3660 objects (000001276A6A84A8->000001276A6AF708) ------------------------------ Heap 1 generation 0 has 85 finalizable objects (000001276A7034D0->000001276A703778) generation 1 has 24 finalizable objects (000001276A703410->000001276A7034D0) generation 2 has 2 finalizable objects (000001276A703400->000001276A703410) Ready for finalization 3845 objects (000001276A703778->000001276A70AFA0) ------------------------------ Heap 2 generation 0 has 56 finalizable objects (000001276A6E6EE0->000001276A6E70A0) generation 1 has 11 finalizable objects (000001276A6E6E88->000001276A6E6EE0) generation 2 has 3 finalizable objects (000001276A6E6E70->000001276A6E6E88) Ready for finalization 3971 objects (000001276A6E70A0->000001276A6EECB8) ... Statistics for all finalizable objects (including all objects ready for finalization): MT Count TotalSize Class Name ... 00007ffcf80cee28 16 896 System.Threading.ThreadPoolWorkQueueThreadLocals 00007ffcf7a95650 21 1512 System.Threading.Thread 00007ffcf80337d0 31 1736 System.Buffers.MemoryPoolBlock 00007ffcf7ab4ab8 20 2240 System.Diagnostics.Tracing.EventSource+OverideEventProvider 00007ffcf80be4e0 76 5472 System.Reflection.Emit.DynamicResolver 00007ffcf81d8378 31881 1020192 BuggyBits.Models.Link Total 32123 objects ```
- What objects are listed in the
!finalizequeue
output? Hint: run!help finalizequeue
All objects that have finalizers/destructors are registered with the finalizer queue so that the finalizer will run the destructor once the object is garbage collected unless finalization is supressed in the dispose method.
- How many objects are “ready for finalization”? What does this mean?
32123 objects. So these objects are garbage collected and ready to be finalized. If ready for finalization shows anything above 0 there is a pretty good chance the finalizer is blocked which is why these objects stick around waiting to be finalized, and with them goes all the memory they reference.
- What objects are listed in the
-
Find the finalizer thread to determine what it is doing. Run
!threads
and look for the thread listed with (Finalizer) -
Move to the finalizer thread and examine the managed and native call stack
~5s # 5 should be substituted with the id of the finalizer thread kb 2000 !clrstack
0:000> !threads ThreadCount: 37 UnstartedThread: 0 BackgroundThread: 20 PendingThread: 0 DeadThread: 16 Hosted Runtime: no Lock DBG ID OSID ThreadOBJ State GC Mode GC Alloc Context Domain Count Apt Exception 21 1 7e54 00000122DDA4E4A0 202a020 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 MTA 31 2 4260 00000122DDDEAD00 202b220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 MTA (Finalizer) 33 3 87e8 00000122DDDEF450 102a220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 MTA (Threadpool Worker) XXXX 4 0 0000012769F3A660 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) 34 6 55f0 0000012769F4A780 8029220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 MTA (Threadpool Completion Port) 35 7 4498 000001276A038F00 202b220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 MTA XXXX 8 0 000001276A3BD8F0 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 9 0 000001276A042910 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) 9 10 1a0c 000001276A424310 20220 Preemptive 00000124E35AFA38:00000124E35B19E0 00000122dde5c0d0 0 Ukn XXXX 11 0 0000012769F37F70 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 12 0 000001276A4F1C70 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 13 0 000001276A4F22A0 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 14 0 000001276A4F28D0 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) 32 15 2834 000001276A3C9F30 20220 Preemptive 00000123E3782380:00000123E37841C0 00000122dde5c0d0 0 Ukn 37 16 5334 000001276A4F1540 202b220 Preemptive 0000012363765648:0000012363765FD0 00000122dde5c0d0 0 MTA XXXX 17 0 000001276A5DD070 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 19 0 000001276A5DAA90 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) 5 18 67e0 000001276A5DD6C0 20220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 6 20 8164 000001276A5DC3D0 20220 Preemptive 00000123E3781D98:00000123E37821C0 00000122dde5c0d0 0 Ukn 8 21 c3c 000001276A5DB730 20220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn XXXX 22 0 000001276A5DCA20 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 23 0 000001276A5DDD10 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 24 0 000001276A5DE360 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 25 0 000001276A5DB0E0 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 26 0 000001276A5DBD80 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 27 0 000001276A57F6E0 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) XXXX 28 0 000001276A57BE10 1039820 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn (Threadpool Worker) 7 29 70d8 000001276A57C460 20220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 4 30 6d90 000001276A57DDA0 20220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 39 31 8490 000001276A57CAB0 21220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 40 32 8644 000001276A582310 21220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 41 33 71d8 000001276A57FD30 21220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 42 34 626c 000001276A5809D0 21220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 43 35 465c 000001276A580380 21220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 44 36 87b0 000001276A57D100 21220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 45 37 63a0 000001276A57EA40 21220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 46 38 8474 000001276A57D750 21220 Preemptive 0000000000000000:0000000000000000 00000122dde5c0d0 0 Ukn 0:000> ~31s ntdll!NtDelayExecution+0x14: 00007ffd`ea54c634 c3 ret 0:031> !clrstack OS Thread Id: 0x4260 (31) Child SP IP Call Site 00000019B1CFF7A8 00007ffdea54c634 [HelperMethodFrame: 00000019b1cff7a8] System.Threading.Thread.SleepInternal(Int32) 00000019B1CFF8A0 00007ffcf83d805c BuggyBits.Models.Link.Finalize() [C:\Tess\source\BuggyBits\src\BuggyBits\Models\Link.cs @ 20] 00000019B1CFFBD0 00007ffd57526b16 [DebuggerU2MCatchHandlerFrame: 00000019b1cffbd0]
- What object is it finalizing?
Looks like it is cleaning up a Link object
- What is it doing? Why is this causing high memory usage?
The finalizer for the Link object is stuck in a sleep which means that the finalizer is blocked so nothing in the process can be finalized, thus all the objects waiting for finalization will continue to stay in memory until the finalizer is unblocked and they can be finalized.
- What object is it finalizing?
Examine the code for verification
- Open Link.cs to find the destructor/finalizer for the Link class to see the problematic code
Performing any type of blocking operations in the finalizer is very dangerous since this causes no objects to be finalized. Apart from checking that you do not perform any blocking operations in the finalizer you should always strive to dispose/close all objects with finalizers/destructors so that you don’t end up in this all too common situation.
Hope you found this useful… the next lab will be a high CPU in GC lab…
Laters, Tess