Automated leak detection in managed code

 

Dan Crevier's Blog

In search of a better name...
Automated leak detection in managed code

Managed code makes memory management much easier, but it's still possible to have unintended memory leaks. Here's an example. In Max, we have a sign in control on our home page. This control register's an event handler with an object that manages the sign in state and then changes its style when the sign in state changes. When you close the main window, we keep the application running in the system tray, and you can open a new window via the system tray. We had a bug where we weren't unregistering from the event handler, so the object that manages sign in state was keeping the sign in UI control alive. This in turn was keeping most of the rest of the UI alive. The solution was to register for the Unloaded event on this Avalon element and unregister from the event handler when the Unloaded event is fired.

In all major native applications I've worked on in the past, there's been some mechanism for automatically detecting leaks. One common practice is to have a custom allocator/deallocator that tracks all allocated memory. Then, when the application quits, if any memory wasn't freed, it asserts that there was a leak. This requires freeing singleton type objects that you might not otherwise free, but it's really important to find leaks as soon as possible. As with all bugs, the sooner you find it, the cheaper it is to fix.

Unfortunately, it's not as straightforward to detect managed leaks. With native code, a leak is well defined. It's memory that's no longer referenced, but hasn't been freed. That type of leak is impossible with managed code. A managed leak is keeping a reference to something that you don't need.

Here's a solution we are using to automatically detect managed leaks. Unfortunately, it's not incredibly general, but maybe it will give you ideas in your application.

First, we have the following helper class:

internal class LeakChecker

{

    public LeakChecker()

    {

        _objectCount++;

    }

 

    ~LeakChecker()

    {

        _objectCount--;

    }

 

    public static void AssertOnLeaks()

    {

        Debug.Assert(_objectCount == 0, "Leaks detected");

    }

 

    private static int _objectCount = 0;

}  

This class increments a static int in its constructor and decrements it in its finalizer. Then, in our debug build, we add a LeakChecker object to several of our UI elements. Then, (again in our debug build only), when closing the main window, we force garbage collection and then call LeakChecker.AssertOnLeaks(). To force garbage collection, do the following:

GC.Collect(GC.MaxGeneration);

GC.WaitForPendingFinalizers();

This will block until garbage collection is done and all fnalizers are run. At this point, if all of the UI objects have been properly freed, there shouldn't be any LeakChecker objects around anymore. So, the assertion should not fire. But, if there's a leak, you'll get an assert when closing the window, so you can find the leak quickly.

This method is far from perfect. For example, if there's an animation going on in the window, the animation system may keep the UI items alive a bit longer. So, you may get some false positives. The solution requires having a time period when you know no objects of some class should exist. But, it's been working pretty well for us so far. Hopefully this will give you some ideas for leak checking in your managed app.

BTW, once you find that you have a leak, you'll need to track it down. Rico Mariani has a great post on using windbg/cdb/ntsd to track them down. I've also been really impressed with .NET Memory Profiler.

Published Tuesday, November 08, 2005 10:08 PM by dancre
Filed under: Debugging

Comments

 
Jochen Kalmbach said:
To force a GC you must call

GC.Collect(GC.MaxGeneration);
GC.WaitForPendingFinalizers();
GC.Collect(GC.MaxGeneration);

(note the last GC.Collect...)
The last call is necessary, because "WaitForPendingFinalizers" will not remove the object from the GC...
November 9, 2005 2:17 AM
New Comments to this post are disabled
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值