Profiling Memory Leaks in Windows Mobile 6.5
This article examines how developers identified and resolved memory leaks in Windows Mobile 6.5 software using specialized performance profiling utilities. Due to the operating system's underlying Windows CE 5.2 kernel architecture—which imposed a strict 32 MB virtual memory limit per process—effective memory tracking was essential to prevent rapid out-of-memory crashes. Below is an overview of the key tools, tracking mechanisms, and diagnostic workflows used to isolate unreleased native and managed memory allocations in the Windows Mobile environment.
The Virtual Memory Challenge in Windows Mobile 6.5
Windows Mobile 6.5 was built on the Windows CE 5.2 core, which partitioned the system into 32 virtual memory slots of 32 MB each. Because one slot was assigned to a given process, exceeding the 32 MB boundary—either through direct allocations or memory fragmentation—would result in an unrecoverable failure. Profiling utilities had to monitor both raw allocation quantities and the address layout of the virtual memory space to detect leaks before they exhausted the slot.
Key Profiling Utilities
Developers primarily relied on three toolsets to isolate leaks:
- Remote Heap Viewer: Part of the Windows Mobile SDK and Visual Studio Remote Tools, this tool monitored the heaps of native processes in real time over an ActiveSync, USB, or Ethernet connection.
- Application Verifier (AppVerifier) for Windows Mobile: A specialized runtime verification tool that hooked allocation functions to detect heap corruption, dangling pointers, and memory leaks.
- CLR Profiler for the .NET Compact Framework: Used specifically for managed C# and VB.NET applications to inspect managed heaps, garbage collection roots, and lingering references.
How Profilers Isolated Memory Leaks
1. API Hooking and Allocation Interception
Profiling tools like Application Verifier injected dynamic link
libraries into the target process at startup. These libraries
substituted standard OS allocation functions (HeapAlloc,
LocalAlloc, VirtualAlloc, and native C/C++
malloc/new) with wrapper functions.
Whenever an allocation occurred, the utility recorded:
- The requested size.
- The memory address returned.
- The timestamp.
- The complete return address call stack of the allocating thread.
When the application or test cycle concluded, the tool checked the
active allocation list. Any address that had not passed through a
corresponding free function (HeapFree,
LocalFree, etc.) was flagged as a leak alongside the
precise call stack that allocated it.
2. Differential Heap Snapshots
The Remote Heap Viewer allowed developers to capture state snapshots
of both the default process heap and any custom heaps created via
HeapCreate.
To isolate leaks using differential analysis:
- The developer exercised the application up to a baseline steady state and triggered a snapshot.
- The developer repeated an isolated user interaction (such as opening and closing a navigation window) several times.
- A subsequent heap snapshot was taken.
- The tool computed a delta between snapshots.
If persistent memory blocks grew in direct proportion to the number of times the action was repeated, the profiler tagged those specific block types and sizes for inspection.
3. Symbol Matching and Call Stack Resolution
To map raw memory addresses to specific lines of source code, the
remote profiling utilities integrated with Program Database
(.pdb) symbol files. When a leaking allocation was
detected, the profiler walked the recorded return addresses and resolved
them against the symbol table, providing the file name, function name,
and line number responsible for the allocation.
4. Managed Memory Root Tracking in the .NET Compact Framework
For applications running on the .NET Compact Framework, leaks were typically caused by abandoned objects remaining rooted in memory, often via unsubscribed event handlers or static collections.
The CLR Profiler interacted directly with the .NET Compact Framework runtime via profiling hooks to:
- Track every garbage collection event and object allocation.
- Construct an object reference graph showing why an object was retained.
- Identify the "retaining path"—the exact chain of references leading from a root (such as a static variable or active thread stack) to the leaked object, preventing the garbage collector from reclaiming it.