Retired Microsoft Blog disclaimer

This directory is a mirror of retired "Decrypt My World" MSDN blog and is provided as is. All posting authorship and copyrights belong to respective authors.

Posts on this page:

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/24/managed-debugging-with-windbg-managed-heap-part-5/
Post name: MANAGED DEBUGGING with WINDBG. Managed Heap. Part 5
Original author: Alejandro Campos Magencio
Posting date: 2009-08-24T05:50:00+00:00


Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Managed Heap. Part 4.

MANAGED HEAP. Part 5

· We can troubleshoot Loader Heap issues:

An assembly can’t be unloaded while the AppDomain where it was created is still alive. We will get a memory leak if we keep creating new assemblies continuously. The problem is that some .NET operations result on new assemblies being created behind the scenes:

§ Create a new XmlSerializer with any constructor but XmlSerializer(type) or XmlSerializer(type, defaultnamespace).

Memory usage is high when you create several XmlSerializer objects in ASP.NET

§ Make XSLT transformations where XSLT Transform contains a script.

PRB: Cannot unload assemblies that you create and load by using script in XSLT

§ Change ASPX, ASCX or ASMX pages which cause re-compilations.

This could be avoided by caching XMLSerializers and XSLT Transforms, and using maxNumRecompiles value which is set to 15 by default.

Imagine that we’ve detected a memory leak in our ASP.NET app, and PerfMon shows that during the time of the memory leak “.NET CLR Loading:Bytes in loader heap” is constantly increasing. The same thing is true for “.NET CLR Loading:Current Assemblies”. We check the number of assemblies in the heap:

0:000> !DumpHeap -stat -type Assembly

------------------------------

Heap 0

total 1141 objects

------------------------------

Heap 1

total 1220 objects

------------------------------

total 2361 objects

Statistics:

MT Count TotalSize Class Name

7912ad0c 1 24 System.Collections.Generic.List`1[[System.Reflection.Assembly, mscorlib]]

...

790fc79c 2297 55128 System.Reflection.Assembly

Total 2361 objects

And we try to get some info about those assemblies:

0:000> !DumpDomain

...

--------------------------------------

Domain 2: 001cb1d8

LowFrequencyHeap: 001cb1fc

HighFrequencyHeap: 001cb258

StubHeap: 001cb2b4

Stage: OPEN

SecurityDescriptor: 001c9ab0

Name: /LM/w3svc/1/ROOT/DebuggingWorkshop-1-128030642795871345

Assembly: 001b6660 [C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll]

ClassLoader: 001773b8

SecurityDescriptor: 642a2e7e

Module Name

790c2000 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll

02272380 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\sortkey.nlp

02272010 C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\sorttbls.nlp

...

Assembly: 0020b220 [C:\WINDOWS\assembly\GAC_MSIL\System.Drawing\2.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll]

ClassLoader: 0020c808

SecurityDescriptor: 642a2e7e

Module Name

7ae72000 C:\WINDOWS\assembly\GAC_MSIL\System.Drawing\2.0.0.0__b03f5f7f11d50a3a\System.Drawing.dll

Assembly: 0020e778 [3k2vmufw, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]

ClassLoader: 0020e800

SecurityDescriptor: 642a2e7e

Module Name

02837b58 3k2vmufw, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

Assembly: 0021c050 [zxsplww6, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]

ClassLoader: 0021c0d8

SecurityDescriptor: 642a2e7e

Module Name

02837f34 zxsplww6, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

...

Assembly: 14bdbf88 [oy_7tsbs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]

ClassLoader: 14bdc010

SecurityDescriptor: 642a2e7e

Module Name

19a273d8 oy_7tsbs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

0:000> !DumpModule 19a273d8

Name: oy_7tsbs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

Attributes: PEFile

Assembly: 14bdbf88

LoaderHeap: 00000000

TypeDefToMethodTableMap: 19bf6158

TypeRefToMethodTableMap: 19bf6170

MethodDefToDescMap: 19bf61dc

FieldDefToDescMap: 19bf6250

MemberRefToDescMap: 19bf62b8

FileReferencesMap: 19bf63fc

AssemblyReferencesMap: 19bf6400

MetaData start address: 19c312d8 (6176 bytes)

0:000> dc 19c312d8 19c312d8+6176

19c312d8 424a5342 00010001 00000000 0000000c BSJB............

...

19c319e8 00010000 0000039b 00000000 6f4d3c00 .............<Mo

19c319f8 656c7564 796f003e 7374375f 642e7362 dule>.oy_7tsbs.d

19c31a08 58006c6c 65536c6d 6c616972 74617a69 ll.XmlSerializat

19c31a18 576e6f69 65746972 72755072 73616863 ionWriterPurchas

19c31a28 64724f65 4d007265 6f726369 74666f73 eOrder.Microsoft

19c31a38 6c6d582e 7265532e 696c6169 6974617a .Xml.Serializati

19c31a48 472e6e6f 72656e65 64657461 65737341 on.GeneratedAsse

19c31a58 796c626d 6c6d5800 69726553 7a696c61 mbly.XmlSerializ

19c31a68 6f697461 6165526e 50726564 68637275 ationReaderPurch

19c31a78 4f657361 72656472 6c6d5800 69726553 aseOrder.XmlSeri

...

We check our code and we can see this:

protected void Page_Load(object sender, EventArgs e)

{

XmlSerializer mySerializer = new XmlSerializer(typeof(PurchaseOrder), new XmlRootAttribute("order"));

...

}

We can see how much memory is taken up by the Loader heap:

0:000> !EEHeap -loader

Loader Heap:

...

--------------------------------------

Module Thunk heaps:

Module 648e8000: Size: 0x0(0)bytes.

Module 68a06000: Size: 0x0(0)bytes.

...

Module 19a273d8: Size: 0x0(0)bytes.

Total size: 0x0(0)bytes

--------------------------------------

Total LoaderHeap size: 0xe33000(14888960)bytes

=======================================

0:000> !DumpModule 19a273d8

Name: oy_7tsbs, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

...

Next post: MANAGED DEBUGGING with WINDBG. Breaking on an Exception. Part 1.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/17/managed-debugging-with-windbg-managed-heap-part-4/
Post name: MANAGED DEBUGGING with WINDBG. Managed Heap. Part 4
Original author: Alejandro Campos Magencio
Posting date: 2009-08-17T02:32:00+00:00


Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Managed Heap. Part 3.

MANAGED HEAP. Part 4

· We can troubleshoot Heap issues:

These are some of the important performance counters we can check with Performance Monitor when having memory issues with .NET applications:

§ Process:Private Bytes: All the memory that the app is using (committed, could be paged). When this value reaches Virtual Bytes, Virtual Bytes doubles its size. If memory is shared, it won’t count as Private Bytes, but as Working Set.

§ Process:Virtual Bytes: Virtual Memory of a process. Memory can be reserved or used (committed, reflected as Private Bytes). This counter is useful when we get Out of Memory exceptions.

§ Process:Working Set: How much memory is paged in RAM.

§ .NET CLR Memory:#bytes in all heaps: .NET won’t use the NT Heap, but saves some virtual memory for its own heap. If this value doesn’t grow but Virtual Bytes do, the memory leak is in the unmanaged world.

§ .NET CLR Memory:#gen 0/1/2 collections.

§ .NET CLR Memory:Gen 0/1/2 Heap Size.

§ .NET CLR Memory:Large Object Heap Size.

§ .NET CLR Memory:# induced GC: Number of calls to GC.Collect. Calling this function in our code is not recommended, as GC auto-adjust for better performance.

§ .NET CLR Memory:# of pinned objects: Number of objects which reference has been blocked (“pinned”) in memory. GC won’t be able to move pinned objects around memory to optimize available space in heap.

§ .NET CLR Memory:finalization survivors: Number of objects which survived a GC because of having a Finalizer method.

§ .NET CLR Memory:Promoted Finalization Memory from Gen 0/1.

§ .NET CLR Memory:Promoted Memory from Gen 0/1.

§ .NET CLR Memory:% Time in GC.

§ .NET CLR Loading:Bytes in Loader Heap: A continuous increase in the size of the Loader Heap is typically an effect of extensive usage of dynamic modules (.NET CLR Loading:Current Assemblies) or having “debug=true” in ASP.NET apps.

§ .NET CLR Loading:Current Assemblies.

§ .NET CLR Loading:Current appdomains.

Additionally, we can generate a log file which can be used to troubleshoot managed memory usage issues. We can take a look to this log file with CLR Profiler:

0:004> !TraverseHeap c:\memory.log

Writing CLRProfiler format to file c:\memory.log

Gathering types...

tracing roots...

Scan Thread 0 OSTHread 1440

Scan Thread 2 OSTHread 194

Walking heap...

....................................................................................................................................................................................

file c:\memory.log saved

We can also check the heap for signs of corruption:

0:004> !VerifyHeap

-verify will only produce output if there are errors in the heap

· We can troubleshoot GCHandle leaks:

Sometimes the source of a memory leak is a GCHandle leak. If we discard a GCHandle which points to i.e. a big array without freeing it, the array will be kept alive.

We can see statistics about GCHandles in the process:

0:004> !GCHandles

GC Handle Statistics:

Strong Handles: 33

Pinned Handles: 8

Async Pinned Handles: 0

Ref Count Handles: 0

Weak Long Handles: 56

Weak Short Handles: 384

Other Handles: 0

Statistics:

MT Count TotalSize Class Name

790fd0f0 1 12 System.Object

...

7b485894 357 22848 System.Windows.Forms.Internal.DeviceContext

7912d8f8 8 28032 System.Object[]

Total 481 objects

We can also try to track down GCHandle leaks. We can search all of memory for any references to the Strong and Pinned GCHandles in the process:

0:004> !GCHandleLeaks

-------------------------------------------------------------------------------

GCHandleLeaks will report any GCHandles that couldn't be found in memory.

Strong and Pinned GCHandles are reported at this time. You can safely abort the

memory scan with Control-C or Control-Break.

-------------------------------------------------------------------------------

Found 41 handles:

001c1118 001c1120 001c1124 001c1148

...

001c13ec 001c13f0 001c13f4 001c13f8

001c13fc

Searching memory

Reference found in stress log will be ignored

Found 001c1188 at location 0030f23c

Found 001c11fc at location 00424fb8

...

Found 001c11d4 at location 7a3b9c88

------------------------------------------------------------------------------

All handles found.

A leak may still exist because in a general scan of process memory SOS can't

differentiate between garbage and valid structures, so you may have false

positives. If you still suspect a leak, use this function over time to

identify a possible trend.

------------------------------------------------------------------------------

Note that if a handle is found, we’ll see the address of the reference. This might be a stack address or a field within an object, for example.

We can see the object a handle references by dereferencing it:

0:004> !do poi(001c1118)

Name: WindowsApplication1.Form1

...

If a handle is not found in memory, we get a notification:

0:004> !GCHandleLeaks

-------------------------------------------------------------------------------

GCHandleLeaks will report any GCHandles that couldn't be found in memory.

Strong and Pinned GCHandles are reported at this time. You can safely abort the

memory scan with Control-C or Control-Break.

-------------------------------------------------------------------------------

Found 249 handles:

01dc1200 01dc1208 01dc1210 01dc1218

...

Searching memory

Found 01dc1ff8 at location 0012ce48

...

------------------------------------------------------------------------------

Some handles were not found. If the number of not-found handles grows over the

lifetime of your application, you may have a GCHandle leak. This will cause

the GC Heap to grow larger as objects are being kept alive, referenced only

by the orphaned handle. If the number doesn't grow over time, note that there

may be some noise in this output, as an unmanaged application may be storing

the handle in a non-standard way, perhaps with some bits flipped. The memory

scan wouldn't be able to find those.

------------------------------------------------------------------------------

Didn't find 232 handles:

01dc1200 01dc1208 01dc1210 01dc1218

...

0:004> !do poi(01dc1218)

...

If a serious leak is going on, we’ll get an ever-growing set of handle addresses that couldn't be found.

Next post: MANAGED DEBUGGING with WINDBG. Managed Heap. Part 5.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/17/managed-debugging-with-windbg-managed-heap-part-3/
Post name: MANAGED DEBUGGING with WINDBG. Managed Heap. Part 3
Original author: Alejandro Campos Magencio
Posting date: 2009-08-17T02:26:00+00:00


Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Managed Heap. Part 2.

MANAGED HEAP. Part 3

· We can take a look to which objects reference which objects in the heap:

We may see many objects in the heap that we think should have been cleaned up already. If they haven’t, it’s because they are still being referenced and GC can’t clean them.

We can look for references (“roots”) to an object. Let’s see this with a sample taken from an ASP.NET app:

0:000> !DumpHeap -stat

...

Statistics:

MT Count TotalSize Class Name

...

0ee10034 19218 7149096 ASP.lab3_1_aspx

...

Total 1748273 objects

0:000> !DumpHeap -mt 0ee10034

------------------------------

Heap 0

Address MT Size

...

0556b268 0ee10034 372

0556ccac 0ee10034 372

total 6524 objects

------------------------------

Heap 1

...

0:000> !GCRoot 0556b268

Note: Roots found on stacks may be false positives. Run "!help gcroot" for

more info.

Scan Thread 15 OSTHread 2c4

...

Scan Thread 13 OSTHread c4c

esi:Root:14f002dc(System.Web.HttpResponse)->

14f00194(System.Web.HttpContext)->

14f00a08(ASP.lab3_1_aspx)->

02b33d80(System.Web.Caching.Cache)->

02b33e60(System.Web.Caching.CacheMultiple)->

02b33e78(System.Object[])->

02b33e90(System.Web.Caching.CacheSingle)->

02b33f38(System.Web.Caching.CacheExpires)->

02b33f58(System.Object[])->

02b34878(System.Web.Caching.ExpiresBucket)->

10872ea0(System.Web.Caching.ExpiresPage[])->

094b5734(System.Web.Caching.ExpiresEntry[])->

0556bb94(System.Web.Caching.CacheEntry)->

0556bb74(System.Web.Caching.CacheItemRemovedCallback)->

0556b268(ASP.lab3_1_aspx)

Scan Thread 22 OSTHread 830

Scan Thread 23 OSTHread f7c

If we take a look to the source code of Lab3_1.aspx page, we can see why the page is being kept in memory by the Cache object:

public partial class Lab3_1 : System.Web.UI.Page

{

public void RemovedCallback(String k, Object v, System.Web.Caching.CacheItemRemovedReason r)

{

//do stuff when the item is removed

}

protected void Page_Load(object sender, EventArgs e)

{

UserInfo u = new UserInfo("James", "Dean", "MainStreet 21, NY");

Cache.Add(Guid.NewGuid().ToString(), u, null, System.Web.Caching.Cache.NoAbsoluteExpiration, new TimeSpan(0, 5, 0), System.Web.Caching.CacheItemPriority.NotRemovable, new System.Web.Caching.CacheItemRemovedCallback(this.RemovedCallback));

}

}

References can exist in several places:

1. In a registry of a thread:

0:022> !gcroot 0x14ef2a34

esi:Root: 14f002dc(System.Web.HttpResponse)->

…->

14ef2a34(ASP.lab3_1_aspx)

2. In the stack of a thread:

ESP:ef4f610:Root: 14f00194(System.Web.HttpContext)->

… ->

14ef2a34(ASP.lab3_1_aspx)

3. Within a GCHandle.

Different kind of handles can reference our objects:

- Strong Handles: they keep the object they point to alive until the handle is explicitly freed. Typically a static variable.

DOMAIN(001CAFF8):HANDLE(Strong):284118c:Root: 02b35d3c(System.Threading._TimerCallback)->

… ->

14ef2a34(ASP.lab3_1_aspx)

- Pinned Handles: they are used to prevent the GC from moving an object during collection. Pinning objects can cause heap fragmentation.

DOMAIN(001CCE68):HANDLE(Pinned)

- Weak Short Handles: they are non null as long as the object they reference is alive because of another Strong reference. They are null otherwise.

DOMAIN(001CCE68):HANDLE(WeakSh)

- Weak Long Handles: they are non null as long as the object they reference hasn’t been reclaimed by the GC. It can be resurrected.

DOMAIN(001CCE68):HANDLE(WeakLn)

The distinction between Weak Short and Weak Long comes from the finalization semantics: a Weak Short reference is null if the object can be freed, but a Weak Long reference still refers to the freachable object.

4. In an object ready for finalization.

The object is no longer in use but nobody called its Dispose method.

Finalizer queue:Root:

5. As a member of an object found in 1, 2 or 3 above.

If our object is referenced by another one, it can’t be garbage collected. We should get rid of that reference or dispose the object that references ours.

14ef3914(System.Web.Caching.CacheEntry)->

14ef38f4(System.Web.Caching.CacheItemRemovedCallback)->

14ef2a34(ASP.lab3_1_aspx)

If there is a live reference to an object, that object is said to be strongly rooted. .NET also introduces the notion of weakly rooted references. A weak reference provides a way for programmers to indicate to the GC that they want to be able to access an object, but they don't want to prevent the object from being collected. Such an object is available until it is collected by the GC. For example, you could allocate a large object, and rather than fully deleting and collecting the object, you could hold onto it for possible reuse, as long as there is no memory pressure to clean up the managed heap. Thus, weak references behave somewhat like a cache.

· We can calculate the size of an object:

We can see the size of an object including its child objects. For instance, to determine how much an ASP.NET app is caching, we can do the following:

0:028> !Name2EE * System.Web.Caching.Cache

...

--------------------------------------

Module: 6638c000 (System.Web.dll)

Token: 0x020000f7

MethodTable: 6639d878

EEClass: 6639d808

Name: System.Web.Caching.Cache

--------------------------------------

...

0:028> !DumpHeap -mt 6639d878

------------------------------

Heap 0

Address MT Size

046b9198 6639d878 12

total 1 objects

------------------------------

Heap 1

Address MT Size

total 0 objects

------------------------------

total 1 objects

Statistics:

MT Count TotalSize Class Name

6639d878 1 12 System.Web.Caching.Cache

Total 1 objects

0:028> !ObjSize 046b9198

sizeof(046b9198) = 319128 ( 0x4de98) bytes (System.Web.Caching.Cache)

Next post: MANAGED DEBUGGING with WINDBG. Managed Heap. Part 4.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/13/managed-debugging-with-windbg-managed-heap-part-2/
Post name: MANAGED DEBUGGING with WINDBG. Managed Heap. Part 2
Original author: Alejandro Campos Magencio
Posting date: 2009-08-13T03:47:00+00:00


Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Managed Heap. Part 1.

MANAGED HEAP. Part 2

· We can take a look to the objects in the heap:

We can see all objects in the heap:

0:004> !DumpHeap

Address MT Size

7b463c40 790fd0f0 12

7b463c4c 790fd8c4 36

7b463c70 790fd8c4 20

...

01905378 79119a1c 32

...

02907de0 003d6d48 16 Free

total 12400 objects

Statistics:

MT Count TotalSize Class Name

7b492254 1 12 System.Windows.Forms.VisualStyles.VisualStyleRenderer+ThemeHandle

...

79119a1c 3 96 System.Security.Policy.PermissionRequestEvidence

...

003d6d48 11 164 Free

...

790fd0f0 165 1980 System.Object

...

790fd8c4 7308 435252 System.String

Total 12400 objects

0:004> !do 01905378

Name: System.Security.Policy.PermissionRequestEvidence

...

0:004> !do 02907de0

Free Object

Size 16(0x10) bytes

Note the existence of Free objects in the heap. The Free entry represents objects which are not referenced and have been garbage collected, but whose memory hasn't been compacted and released to the operating system yet. These regions of space can be re-used by the Garbage Collector later. If 30% or more of the heap contains Free objects, the process may suffer from heap fragmentation.

I’m sure we noticed that there are too many objects in the heap, so it’s usually better to see only the statistics:

0:004> !DumpHeap -stat

total 12400 objects

Statistics:

MT Count TotalSize Class Name

7b492254 1 12 System.Windows.Forms.VisualStyles.VisualStyleRenderer+ThemeHandle

...

79119a1c 3 96 System.Security.Policy.PermissionRequestEvidence

...

003d6d48 11 164 Free

...

790fd0f0 165 1980 System.Object

...

790fd8c4 7308 435252 System.String

Total 12400 objects

We can also focus on the objects of a given type only:

0:004> !Name2EE * System.Security.Policy.PermissionRequestEvidence

Module: 790c2000 (mscorlib.dll)

Token: 0x0200048a

MethodTable: 79119a1c

...

0:004> !DumpHeap -mt 79119a1c

Address MT Size

01901ebc 79119a1c 32

01905378 79119a1c 32

019290c0 79119a1c 32

total 3 objects

Statistics:

MT Count TotalSize Class Name

79119a1c 3 96 System.Security.Policy.PermissionRequestEvidence

Total 3 objects

Or focus on objects of several types with part of the name in common:

0:004> !DumpHeap -type System.Security.Policy

Address MT Size

01901b78 7910ed68 20

...

01901ebc 79119a1c 32

...

0192971c 79119bf4 16

total 38 objects

Statistics:

MT Count TotalSize Class Name

791197b0 1 12 System.Security.PolicyManager

7911c924 1 24 System.Security.Policy.Hash

...

79119a1c 3 96 System.Security.Policy.PermissionRequestEvidence

...

790f97c4 3 156 System.Security.Policy.PolicyLevel

Total 38 objects

We can see the objects in the Large Object Heap, and we can do it in several ways:

0:004> !EEHeap -GC

Number of GC Heaps: 1

generation 0 starts at 0x01901018

generation 1 starts at 0x0190100c

generation 2 starts at 0x01901000

ephemeral segment allocation context: none

segment begin allocated size

00423878 7b463c40 7b47a744 0x00016b04(92932)

0041d178 7a733370 7a754b98 0x00021828(137256)

003d6600 790d8620 790f7d8c 0x0001f76c(128876)

01900000 01901000 01949ff4 0x00048ff4(298996)

Large object heap starts at 0x02901000

segment begin allocated size

02900000 02901000 02907df0 0x00006df0(28144)

Total Size 0xa787c(686204)

------------------------------

GC Heap Size 0xa787c(686204)

0:004> !DumpHeap 02901000 02907df0

Address MT Size

02901000 003d6d48 16 Free

02901010 7912d8f8 4096

...

total 15 objects

Statistics:

MT Count TotalSize Class Name

003d6d48 8 128 Free

7912d8f8 7 28016 System.Object[]

Total 15 objects

0:004> !SOS.DumpHeap -min 85000

...

Note that there are objects in LOH which size is smaller than 85,000 bytes (?).

We can also see the objects which are ready for finalization, and in general, which objects in our app have a Finalize method:

0:004> !FinalizeQueue

SyncBlocks to be cleaned up: 0

MTA Interfaces to be released: 0

STA Interfaces to be released: 0

----------------------------------

generation 0 has 703 finalizable objects (00457998->00458494)

generation 1 has 0 finalizable objects (00457998->00457998)

generation 2 has 0 finalizable objects (00457998->00457998)

Ready for finalization 0 objects (00458494->00458494)

Statistics:

MT Count TotalSize Class Name

7b492254 1 12 System.Windows.Forms.VisualStyles.VisualStyleRenderer+ThemeHandle

...

7ae77fa8 2 80 System.Drawing.Icon

...

7b485894 59 3776 System.Windows.Forms.Internal.DeviceContext

Total 703 objects

0:004> dd 00457998 00458494-4

00457998 01901f10 01905238 01906414 01906428

...

00457b28 019323f8 019324b0 0193296c 01936378

...

00458488 01949468 019494f0 0194959c

0:004> !DumpHeap -mt 7ae77fa8

Address MT Size

0193296c 7ae77fa8 40

01936378 7ae77fa8 40

total 2 objects

Statistics:

MT Count TotalSize Class Name

7ae77fa8 2 80 System.Drawing.Icon

Total 2 objects

0:004> !do 0193296c

Name: System.Drawing.Icon

...

0:004> !DumpMT -md 7ae77fa8

...

Name: System.Drawing.Icon

...

--------------------------------------

MethodDesc Table

Entry MethodDesc JIT Name

...

7ae2070c 7aea3c88 PreJIT System.Drawing.Icon.Finalize()

...

7ae201dc 7aea3c58 PreJIT System.Drawing.Icon.Dispose()

...

Look to the way Reflector.exe shows the Dispose and Finalize (destructor) methods of the “Icon” class we saw above, and how Dispose tells the GC to forget about the Finalize method:

public void Dispose()

{

this.Dispose(true);

GC.SuppressFinalize(this);

}

~Icon()

{

this.Dispose(false);

}

We can get extra information on the following objects registered for finalization: SyncBlocks and RuntimeCallableWrappers (RCWs). Both of these data structures are cached and cleaned up by the Finalizer thread.

0:000> !FinalizeQueue -detail

To be cleaned Com Data

0x4edc1b8 ComPlusWrapper

0x4edc590 ComPlusWrapper

...

0x4e227b8 ComPlusWrapper

SyncBlock to be cleaned up: 6371

MTA interfaces to be released: 0

STA interfaces to be released: 0

...

Every time a COM interface pointer enters the CLR, it is wrapped in a RCW. The RCW has a reference count that is incremented every time a COM interface pointer is mapped to it. The System.Runtime.InteropServices.Marshal.ReleaseComObject method decrements the reference count. If the same COM interface is passed more than once from unmanaged to managed code, the reference count on the wrapper is incremented every time and calling ReleaseComObject returns the number of remaining references. When the reference count reaches zero, the runtime releases all its references on the unmanaged COM object. We use this method to free the COM object and the resources it holds.

Next post: MANAGED DEBUGGING with WINDBG. Managed Heap. Part 3.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)

Original URL: https://blogs.msdn.microsoft.com/alejacma/2009/08/13/managed-debugging-with-windbg-managed-heap-part-1/
Post name: MANAGED DEBUGGING with WINDBG. Managed Heap. Part 1
Original author: Alejandro Campos Magencio
Posting date: 2009-08-13T03:33:00+00:00


Hi all,

This post is a continuation of MANAGED DEBUGGING with WINDBG. Thread Stacks. Part 2.

MANAGED HEAP. Part 1

.NET won’t use the NT Heap. It saves some virtual memory for its own managed heap instead.

Objects in the managed heap are part of a Garbage Collector (GC) generation. We have Gen 0, 1 & 2.

All objects start in Gen 0 unless they are over 85,000 bytes in which case they end up in the Large Object Heap (LOH, also known as Gen 3). 85,000 bytes refers to the actual size of the object, not including child objects (member variables). If a member of an object is a reference to another object, the size of that reference is only 4 bytes (in 32 bit machines). Most commonly the items that we will see in the Large Object Heap are strings and large arrays.

When an object is allocated, the GC checks if we have reached one of the generational limits. If so, a GC is triggered. The generational limits are dynamically changed throughout the life of the process in order to better suit the allocation pattern of the application.

If an object is still referenced (“rooted”) during a garbage collection, it is promoted/moved into the next higher generation. The exception is objects in Gen 2 and LOH. Garbage collections on Gen 2 are less frequent than on Gen 1, and on Gen 1 than on Gen 0.

We use LOH for large objects for two main reasons: 1) it is assumed that if you allocate a large object, you are likely to keep it around for a while, 2) large objects are expensive to move. For these reasons LOH is collected less frequently, together with Gen2. Additionally, LOH is not compacted. Use LOH wisely!

Note that if an object is not referenced but has a Finalize (destructor) method, it will automatically get promoted to the next gen and clean up in that gen, unless we call its Dispose method. Dispose will call Finalize and instruct the GC to forget about it (by calling GC.SuppressFinalizer) and clean up the object in its current gen. Calling Dispose guarantees we clean up an object ASAP.

· We can take a look to the .NET heap:

We can see how much memory is taken up by the managed heap and its parts: generations, large object heap, etc.:

0:004> !EEVersion

2.0.50727.1433 retail

Workstation mode

SOS Version: 2.0.50727.42 retail build

0:004> !EEHeap -gc

Number of GC Heaps: 1

generation 0 starts at 0x01901018

generation 1 starts at 0x0190100c

generation 2 starts at 0x01901000

ephemeral segment allocation context: none

segment begin allocated size

00423878 7b463c40 7b47a744 0x00016b04(92932)

0041d178 7a733370 7a754b98 0x00021828(137256)

003d6600 790d8620 790f7d8c 0x0001f76c(128876)

01900000 01901000 01949ff4 0x00048ff4(298996)

Large object heap starts at 0x02901000

segment begin allocated size

02900000 02901000 02907df0 0x00006df0(28144)

Total Size 0xa787c(686204)

------------------------------

GC Heap Size 0xa787c(686204)

Note that workstation applications will only have 1 heap (see above), but server applications (ASP.NET) will have 1 heap per processor (HyperThreading aware).

0:028> !EEVersion

2.0.50727.1433 retail

Server mode with 2 gc heaps

SOS Version: 2.0.50727.42 retail build

0:028> !EEHeap -gc

Number of GC Heaps: 2

------------------------------

Heap 0 (02ec2c48)

generation 0 starts at 0x04722600

...

Heap Size 0xc8048(819272)

------------------------------

Heap 1 (02ec38e0)

generation 0 starts at 0x086f4264

...

Heap Size 0x4a248(303688)

------------------------------

GC Heap Size 0x112290(1122960)

In workstation apps, GC will take place in the thread that tries to allocate memory and can’t. In server apps there will be one dedicated GC thread per heap/processor.

· We can check if Preemptive GC mode is enabled on a thread:

0:004> !Threads

ThreadCount: 2

UnstartedThread: 0

BackgroundThread: 1

PendingThread: 0

DeadThread: 0

Hosted Runtime: no

PreEmptive GC Alloc Lock

ID OSID ThreadOBJ State GC Context Domain Count APT Exception

0 1 1d14 005a6e80 6020 Enabled 0299c3e8:0299dfe8 00571520 0 STA

2 2 1b34 005a8e00 b220 Enabled 00000000:00000000 00571520 0 MTA (Finalizer)

Preemptive GC indicates what GC mode the thread is in: "enabled" in the table means the thread is in preemptive mode where GC could preempt this thread at any time; "disabled" means the thread is in cooperative mode where GC has to wait the thread to give up its current work (the work is related to GC objects so it can't allow GC to move the objects around). When the thread is executing managed code (the current IP is in managed code), it is always in cooperative mode; when the thread is in Execution Engine (unmanaged code), EE code could choose to stay in either mode and could switch mode at any time; when a thread is outside of CLR (i.e. calling into native code using interop), it is always in preemptive mode.

Summing up, if a thread has PreEmptive GC disabled is because it does not want to be interrupted by the GC.

Next post: MANAGED DEBUGGING with WINDBG. Managed Heap. Part 2.

Index: MANAGED DEBUGGING with WINDBG. Introduction and Index.

Regards,

Alex (Alejandro Campos Magencio)