Analyzing the root cause of an OutOfMemoryException in a WPF application using WinDbg

I am having trouble understanding a crash dump and figuring out what is the main cause of the OutOfMemoryException by the WPF application. An exception occurs after the application has been running for several hours, so this clearly indicates a memory leak.

My first step was to look at the command !address -summary :

 --- Usage Summary ---------------- RgnCount ------- Total Size -------- %ofBusy %ofTotal <unknown> 2043 58997000 ( 1.384 Gb) 71.43% 69.22% Heap 152 fcc3000 ( 252.762 Mb) 12.74% 12.34% Image 1050 bc77000 ( 188.465 Mb) 9.50% 9.20% Stack 699 7d00000 ( 125.000 Mb) 6.30% 6.10% Free 518 3f6b000 ( 63.418 Mb) 3.10% TEB 125 7d000 ( 500.000 kb) 0.02% 0.02% Other 12 36000 ( 216.000 kb) 0.01% 0.01% PEB 1 1000 ( 4.000 kb) 0.00% 0.00% --- Type Summary (for busy) ------ RgnCount ----------- Total Size -------- %ofBusy %ofTotal MEM_PRIVATE 2186 685b7000 ( 1.631 Gb) 84.14% 81.53% MEM_IMAGE 1710 f3f3000 ( 243.949 Mb) 12.29% 11.91% MEM_MAPPED 186 46db000 ( 70.855 Mb) 3.57% 3.46% --- State Summary ---------------- RgnCount ----------- Total Size -------- %ofBusy %ofTotal MEM_COMMIT 3366 73fe7000 ( 1.812 Gb) 93.52% 90.62% MEM_RESERVE 716 809e000 ( 128.617 Mb) 6.48% 6.28% MEM_FREE 518 3f6b000 ( 63.418 Mb) 3.10% --- Protect Summary (for commit) - RgnCount ----------- Total Size -------- %ofBusy %ofTotal PAGE_READWRITE 1650 5e19e000 ( 1.470 Gb) 75.87% 73.52% PAGE_EXECUTE_READ 224 bc42000 ( 188.258 Mb) 9.49% 9.19% PAGE_READWRITE|PAGE_WRITECOMBINE 28 439f000 ( 67.621 Mb) 3.41% 3.30% PAGE_READONLY 573 3d7b000 ( 61.480 Mb) 3.10% 3.00% PAGE_WRITECOPY 214 f8f000 ( 15.559 Mb) 0.78% 0.76% PAGE_EXECUTE_READWRITE 265 d0a000 ( 13.039 Mb) 0.66% 0.64% PAGE_READWRITE|PAGE_GUARD 357 33b000 ( 3.230 Mb) 0.16% 0.16% PAGE_EXECUTE_WRITECOPY 55 119000 ( 1.098 Mb) 0.06% 0.05% --- Largest Region by Usage ----------- Base Address -------- Region Size ---------- <unknown> 78d40000 2350000 ( 35.313 Mb) Heap 36db0000 fd0000 ( 15.813 Mb) Image 64a8c000 e92000 ( 14.570 Mb) Stack 4b90000 fd000 (1012.000 kb) Free 7752f000 1a1000 ( 1.629 Mb) TEB 7ede3000 1000 ( 4.000 kb) Other 7efb0000 23000 ( 140.000 kb) PEB 7efde000 1000 ( 4.000 kb) 

This shows that the memory is quite high.

Then I view the GC heap size with the eeheap -gc . This shows that the heap is quite large (1.1 GB), which indicates a problem in the managed part of the application.

 5fc90000 5fc91000 60c7acd4 0xfe9cd4(16686292) 5a060000 5a061000 5b05e9c0 0xffd9c0(16767424) 56de0000 56de1000 57ddf1c4 0xffe1c4(16769476) 57de0000 57de1000 58ddbbbc 0xffabbc(16755644) 73ff0000 73ff1000 74fe0f5c 0xfeff5c(16711516) 50de0000 50de1000 51dcfa58 0xfeea58(16706136) 5b060000 5b061000 5c05ca54 0xffba54(16759380) 4fde0000 4fde1000 50ddfd8c 0xffed8c(16772492) Large object heap starts at 0x03921000 segment begin allocated size 03920000 03921000 049013d0 0xfe03d0(16647120) 14850000 14851000 15837380 0xfe6380(16671616) 178d0000 178d1000 1889a3e0 0xfc93e0(16552928) 1a1c0000 1a1c1000 1b1abca8 0xfeaca8(16690344) 40de0000 40de1000 41dc8b48 0xfe7b48(16677704) 42de0000 42de1000 43827170 0xa46170(10772848) 54de0000 54de1000 55dd6d18 0xff5d18(16735512) Total Size: Size: 0x448fde94 (1150279316) bytes. ------------------------------ GC Heap Size: Size: 0x448fde94 (1150279316) bytes. 

Please note that there are 64 segments and each is about (16 MB). It seems that some data is stored in memory and has never been released.

Next I look !dumpheap -stat :

 65c1f26c 207530 19092760 System.Windows.Media.GlyphRun 65c2c434 373991 20943496 System.Windows.Media.RenderData 68482bb0 746446 26872056 MS.Utility.ThreeItemList`1[[System.Double, mscorlib]] 65c285b4 746448 29857920 System.Windows.Media.DoubleCollection 64c25d58 299568 32353344 System.Windows.Data.BindingExpression 6708a1b8 2401099 38417584 System.WeakReference 67082c2c 1288315 41226080 System.EventHandler 67046f80 1729646 42238136 System.Object[] 64c1409c 206969 52156188 System.Windows.Controls.ContentPresenter 67094c9c 382163 64812664 System.Byte[] 004b0890 159 65181140 Free 64c150d0 207806 72316488 System.Windows.Controls.TextBlock 6708fd04 1498498 97863380 System.String 6848038c 847783 128775772 System.Windows.EffectiveValueEntry[] 

As I understand it, there is no signle object that takes up all the memory. The largest is only 122 MB. Summing all sizes (8,500 lines of selected lines) gives (1.1 GB) of used memory. It seems that the entire graphic object is somehow duplicated and added to memory and never released.

!gcroot 6848038c or !gcroot 6708fd04 to check how EffectiveValueEntry and System.String are reachable, never run out, the stack has soooo big ...

dumpheap -mt <address> doesn't show me what annoys me. !finalizequeue shows that many objects (over 2 million) are registered for finalization:

 6708a1b8 2401099 38417584 System.WeakReference Total 2417538 objects 

I suspect that an OutOfMemoryException occurs when an application tries to duplicate an object graph and allocates new memory, but I cannot find its root cause.

Question How can I get to the root of the problem (which other windbg command can I use to check). It seems that not only one object is leaking, but the entire object chart. Am I on the right track or is there something else I'm missing? What is another hypothesis?

+5
source share
2 answers

Duplicate object graph

Your application uses ~ 1.1 GB of virtual memory from .NET. You can see that from the exit !eeheap -gc directly

 GC Heap Size: Size: 0x448fde94 (1150279316) bytes. 

or by summing the values !dumpheap -stat .

Summing up all sizes (8500 lines of output lines) gives (1.1 GB)

This roughly corresponds to the value displayed as <unknown> in !address -summary .

 --- Usage Summary ---------------- RgnCount ------- Total Size -------- %ofBusy %ofTotal <unknown> 2043 58997000 ( 1.384 Gb) 71.43% 69.22% 

There is no reason to assume that the entire object graph is duplicated. This is a normal situation.

Outofmemory

At the moment, there is 65 MB of virtual memory already allocated by .NET and marked as free (from !dumpheap -stat ):

 004b0890 159 65181140 Free 

Unfortunately, these 65 MB are divided into 159 small regions. to get the largest block of them, you need to run !dumpheap -mt 004b0890 .

In addition, .NET can get another 63 MB from Windows (from !address -summary ):

 --- Usage Summary ---------------- RgnCount ------- Total Size -------- Free 518 3f6b000 ( 63.418 Mb) 

But the largest block is only 1.6 MB, so it's almost useless:

 --- Largest Region by Usage ----------- Base Address -------- Region Size ---------- Free 7752f000 1a1000 ( 1.629 Mb) 

So, obviously, the application has lost memory.

Where is the memory located?

252 MB are in the native heap. You seem to be using some native dlls. Although at the moment this does not seem too strong, this fact may indicate the presence of fixed objects. Attached objects are not collected with garbage. Look at the output !gchandles to find out if this might be part of the problem.

188 MB are in the DLL. You can unload your own DLL files that are not used, but for .NET collections you probably can't do much.

125 MB are in packages. With a default size of 1 MB, it seems that your application has 125 threads. Use !clrstack to find out what they are doing and why they are not done yet. Potentially, each thread is working on something and has not yet freed objects. If you have this under your control, do not start so many threads in parallel. For instance. use only 8 threads and wait for the threads to complete before doing the next part of the work.

Of course, most of the memory is used by .NET objects. However, you have made several incorrect conclusions.

As I understand it, there is not a single object that takes up all the memory. The largest is only about 122 MB.

Note that there are no EffectiveValueEntry[] objects containing 122 MB of memory. Of these, 847,783. This changes the question: "Why does this object use so much memory?" "Why are there so many?" For example, why does your application need text blocks 207.806? Does it really display so much text?

Using !gcroot is a good idea. However, you used it with the address of the method table instead of the object:

 !gcroot 6848038c !gcroot 6708fd04 

These were both numbers from the output !dumpheap -stat . Using them in !gcroot should give a warning, for example

Please note that 6848038c is not a valid object.

Instead !gcroot only works with individual objects that you get from !dumpheap without the -stat parameter.

You can use !traveseheap filename.log to delete all objects in a file compatible with the CLR profiler [Codeplex] . Note that the CLR Profiler cannot read the -xml format. After loading the Heap Graph object information, it’s probably the most useful button for you.

To find out about an OutOfMemoryException exception trigger, you can use the !u command. You will need to read the MSIL code to understand what it does. See Also How to determine the type of an array . However, in your scenario, I assume this is useless, because even small objects can cause this.

+9
source

In addition to another answer, you can try to visualize memory addresses and a bunch of GC with the extension WinDbg cosos gcview

0
source

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


All Articles