Mobile Zone is brought to you in partnership with:

Sasha Goldshtein is a Senior Consultant for Sela Group, an Israeli company specializing in training, consulting and outsourcing to local and international customers.Sasha's work is divided across these three primary disciplines. He consults for clients on architecture, development, debugging and performance issues; he actively develops code using the latest bits of technology from Microsoft; and he conducts training classes on a variety of topics, from Windows Internals to .NET Performance. You can read more about Sasha's work and his latest ventures at his blog: http://blogs.microsoft.co.il/blogs/sasha. Sasha writes from Herzliya, Israel. Sasha is a DZone MVB and is not an employee of DZone and has posted 204 posts at DZone. You can read more from them at their website. View Full User Profile

Diagnosing Memory Leaks in Managed Windows Store Apps

10.18.2012
| 3751 views |
  • submit to reddit

There is so much material on the web (and even on this blog) about memory leak diagnostics in managed code, and a considerable number of tools that make diagnostics increasingly easier. Modern memory profilers can open application dumps, attach to live processes, display live memory graphs, compare snapshots, identify problematic retention patterns, and so much more.

Unfortunately, these tools presently don’t work with Windows Store apps. Moreover, the UI model of Windows Store apps poses a significant challenge in diagnosing many UI-related memory leaks, such as composite UI controls retaining objects embedded in them, or UI elements retaining objects registered to their events.

Namely, the Windows Store app UI framework is implemented in unmanaged code and exposed to C# apps through fairly standard COM interop (with some minor tweaks). Because the garbage collector has no insight into or control over unmanaged COM objects in the Windows Runtime, tracing a memory leak to its true source becomes exceptionally difficult. Needless to say, the lack of profiler support does not help – you need to pull the trusty WinDbg and SOS combination for even the simplest of problems.

To begin with, you can’t reliably keep WinDbg attached to a Windows Store app. It seems that Windows detects that the app is not running, and eventually terminates it under the nose of the debugger. This isn’t that big a deal – you have to resort to dump files instead. (On Windows 8, Task Manager can capture dumps for you; just make sure you capture dumps of 32-bit apps using the 32-bit Task Manager, and vice versa.)

image

Consider an app that consumes a large amount of memory because it has a ListView with many large objects retained by it. The standard leak diagnostic commands produce the following:

image

0:006> !dumpheap -stat
…edited for brevity…
3077ce04       14          224 Windows.UI.Xaml.Controls.ListBoxItem
3077ee2c       15          240 Windows.UI.Xaml.Data.Binding
005e5f00       14          392 MemoryLeakingStoreApp.BigObject
5b6d7940        7          420 System.Reflection.RuntimeMethodInfo
5b6d7180        5          420 System.RuntimeType+RuntimeTypeCache
5b6a1e64       18          432 System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal+NativeOrStaticEventRegistrationImpl+EventRegistrationTokenListWithCount
309d0af4       14          448 Windows.UI.Xaml.Controls.SelectionChangedEventHandler
5ab845ec       17          476 System.UriParser+BuiltInUriParser
5b6a1094        5          600 System.Runtime.CompilerServices.ConditionalWeakTable`2+Entry[[System.Object, mscorlib],[System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal+NativeOrStaticEventRegistrationImpl+EventRegistrationTokenListWithCount, mscorlib]][]
5b6dd690        2          616 System.Globalization.CultureData
5ab845a4        2          952 System.Collections.Generic.Dictionary`2+Entry[[System.String, mscorlib],[System.UriParser, System]][]
5b6dc738       15          988 System.Int32[]
5b6a0d40       94         1504 System.Runtime.InteropServices.WindowsRuntime.ICustomPropertyProviderProxy`2[[System.Object, mscorlib],[System.Object, mscorlib]]
5b6dc168       55         1540 System.RuntimeType
5b6dafb0      205         9996 System.String
5b68ae88       30        18076 System.Object[]
008a0dd8      125        32450      Free
5b6dd244       29     21000348 System.Byte[]

OK, so at this point we know that we have a small number of byte arrays that consume a bunch of memory. A typical root chain for the larger ones looks like this:

0:006> !gcroot 04393620
HandleTable:
    00541478 (ref counted handle)
    -> 025471e8 System.Runtime.InteropServices.WindowsRuntime.ICustomPropertyProviderProxy`2[[System.Object, mscorlib],[System.Object, mscorlib]]
    -> 02546bf8 Windows.UI.Xaml.Controls.ItemCollection
    -> 0254b734 MemoryLeakingStoreApp.BigObject (dependent handle)
    -> 04393620 System.Byte[]

That is to say, the byte arrays are retained by BigObject instances, which are held by a XAML ItemsCollection. But which objects holds this collection? It remains a mystery behind the ICustomPropertyProviderProxy generic interop interface… Now surely, ItemsCollection points in the general direction of the user interface, and hints pretty strongly that some kind of items control is involved. But there is no way to determine which – the mysterious answer is behind the Great Wall of Interop.

Similarly, suppose you have a bunch of objects registered for an event of a UI control, such as ListView.SelectionChanged. Here’s what the reference chain will look like in the debugger:

0:006> !gcroot 0c0935d0
HandleTable:
    005418f8 (ref counted handle)
    -> 0254d22c Windows.UI.Xaml.Controls.SelectionChangedEventHandler
    -> 0254d220 MemoryLeakingStoreApp.MyHandler
    -> 0c0935d0 System.Byte[]

As before, the useful part terminates at the SelectionChangedEventHandler object. It is retained by a CCW (COM-callable wrapper), and you don’t know how to proceed behind the Great Wall. Yes, you have the hint of “SelectionChangedEventHandler”, but if you have many UI controls on the page, or pages that your app may have retained from past navigations, this still doesn’t help much.

Interestingly, you can always discern between an event handler (or any CCW reference in fact) retained by the XAML framework (“Jupiter”) as opposed to a standard CCW reference from COM or a non-UI Windows Runtime component. To do so, find the CCW address for your managed object and run !dumpccw. For example:

0:000> !do -nofields 02028c28
Name:        MemoryLeakingStoreApp.BigObject
MethodTable: 003d5f00
EEClass:     05d0179c
CCW:         0065f240
Size:        28(0x1c) bytes

0:000> !dumpccw 0065f240
Managed object:    02028c28
Outer IUnknown:    00000000
Ref count:         0
Flags:            
Jupiter ref count: 8, Pegged by Jupiter & CLR
RefCounted Handle: 002d17f8 (STRONG)
COM interface pointers:
      IP       MT Type

To conclude, memory leak diagnostics have just become much more interesting (read: more difficult) with Windows Store apps, especially when any Windows Runtime objects are involved. Because your C# code is now playing in a largely-unmanaged field, with COM objects retaining .NET objects, which in turn retain other COM objects, there’s plenty of new types of leaks and nasty interop scenarios to be worried about.

There hasn’t ever been a better time to take an “Introduction to COM” course since the late ‘90s :-)

 

 

 

Published at DZone with permission of Sasha Goldshtein, author and DZone MVB. (source)

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)