Detecting Application Memory leaks in Unmanaged C++ with 4 lines of code:

98 篇文章 0 订阅

We have customers who suspect memory leaks in our APIs.  Their only clues are that memory grows and that they are using one of our APIs.  One big problem is finding out if it’s their code or our API. Well, we need try doing a simple repro using our API – this gives us a better idea of what’s going on. However it’s not always clear if an API having the problem under or their code.  There is a simple technique to seeing if there is a leak in their code by adding a few lines of code.   

 

    Debugging Visual C++ - General debugging info

    http://msdn2.microsoft.com/en-us/library/k70yt3e2(vs.71).aspx

 

    Enabling Memory Leak Detection - this work, please try them

    http://msdn2.microsoft.com/en-us/library/e5ewb1h3(vs.71).aspx

 

Here is how it works:

 

1)     Add the following to the top of your code – this will cause debug versions of new and delete, etc to be used.

     #define CRTDBG_MAP_ALLOC  

     #include <stdlib.h>  

    #include <crtdbg.h>    

 

2)     Now at a place where you expect everything created to have been destroyed, add the line below in the code. 

      This line will print memory leak information to the output window:

   _CrtDumpMemoryLeaks();  // dump memory leaks to the output window

 

3)     Set a break point on _CrtDumpMemoryLeaks(); and run the code.

 

4)     Now step though _CrtDumpMemoryLeaks(); and you will get something like the following in the output window.  These are the items for which memory had been allocated and not yet released.  You might want to copy the output to notepad for future reference.

 

Detected memory leaks!

Dumping objects ->

{64} normal block at 0x00BE2DD8, 12 bytes long.

 Data: <            > C4 16 16 00 00 00 00 00 01 00 00 00

{59} normal block at 0x00BE2D48, 12 bytes long.

 Data: <|           > 7C FC 15 00 00 00 00 00 01 00 00 00

{54} normal block at 0x00BE2EB0, 2048 bytes long.

 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD

{53} normal block at 0x00BE2E68, 12 bytes long.

 Data: <|           > 7C 85 13 00 00 00 00 00 01 00 00 00

{47} normal block at 0x00BE2D00, 12 bytes long.

 Data: <            > AC 83 13 00 00 00 00 00 01 00 00 00

{46} normal block at 0x00BE2CB8, 12 bytes long.

 Data: <t           > 74 83 13 00 00 00 00 00 01 00 00 00

Object dump complete.

 

    Looking at the first entry, you will see:

   {64} normal block at 0x00BE2DD8, 12 bytes long.

        Data: <            > C4 16 16 00 00 00 00 00 01 00 00 00

        

            The “{64}” is the memory allocation occurrence. So, there were 63 memory allocations

            which already occurred.

            “0x00BE2DD8” is the memory address of the allocation.

The “Data: <            >” shows the first 12 bytes of data at that memory space – so if it’s a

string data, then you would see text.

“C4 16 16 00 00 00 00 00 01 00 00 00” is for those 12 bytes in hex.

 

So, at the point that the leak dump was done above, we know that some stuff had memory allocations but was not unallocated… so what does this give us?  The main key here is the memory allocation instance – like the {64} in the above example.  You can set a breakpoint at that memory allocation – below are two methods for doing this:

 

1)     Do it in code by adding a line setting a breakpoint at that allocation:

   _crtBreakAlloc = 64; // Break at the 64th memory allocation.

     Or

   _CrtSetBreakAlloc(64);  // Break at the 64th memory allocation.

 

2)     Do it in the Watch window:

Run the app in debug and add “_crtBreakAlloc” under the Name column. 

In the Value column, enter the allocation instance – in the example above, its 64.

Please note that the allocation instance seems to reset to -1, so you may need to reset it at times.

 

When you run the code, you will be prompted to break – do so.  You will see that you stopped at a line like the following in dbgheap.c:

           /* break into debugger at specific memory allocation */

        if (_crtBreakAlloc != -1L && lRequest == _crtBreakAlloc)

            _CrtDbgBreak();

 

From here, step-out until you get back to your source file – you will end-up at the line at which the memory allocation was done – so you know what was leaking now.  From here you just need to find out why it was not released.

 

Checking for leaks when you don’t have the code:

 

    UMDH

    How to Use Umdh.exe to Find Memory Leaks

    http://support.microsoft.com/kb/268343/EN-US/

 

What causes leaks?

 

... Ok, here are a few causes...

 

1)     If you create something with a New, you should release it.

In C++, you would have a “delete” for each “new”. In C++, never mix malloc and/or free with new/delete – mixing may cause a leak.  Also, malloc/free don’t fire the constructors or destructors. 

In VB.NET, you would set an object to “nothing” if it was created with a new.

In C# you would set an object to “null” if it was created with a new.

 

2)     If you open it, you should close it.

This goes for all things which can be opened.  If there is an “open” method, there is most likely a “close”.  Don’t rely on things to be closed when they fall out of scope – that’s sloppy.

 

3)     If you reference it, dereference it.

If you have reference objects, pointers, etc to stuff, you should dereference it properly.  Don’t assume that it won’t matter because of scope, etc.

 

So, set it to null, noting, etc.

 

4)     If its VC++/COM object, call ->Release() as needed.  An example of this would be to call on a bodypart to be sure it releases.

 

5)   Assuming destruction.

Magic does not always happen as expected; don’t assume that things will magically get cleaned-up by losing scope or closing something.  Add some code and walk through it with the debugger – be sure you get what you expect.

 

6)   .NET – COM Resurrection, GC.Collect, Marshal.ReleaseComObject

This is .NET magic… Com items created do not get destroyed automatically when dereferenced.  Were you assuming they did?  If a COM component is created and then dereferenced, it hangs around for a while unless you call ReleaseComObject on it.  If you don't get rid of it, .NET may re-use the object (COM Ressurection) - which in some cases may cause problems.

 

The Garbage Collector takes care of destruction at a time it deems it should - or is that true? Calling GC.Collect will force an application to do all garbage collection at one time- mass destruction - hmmm is that true.  Both are not really correct.  You can force collection on a particular item by calling ReleaseComObject if the object is COM based.  An example of this would be a CDOSYS bodypart for an attachment.  You should be sure to call System.Runtime.InteropServices.Marshal.ReleaseComObject(oBP); to force its release.  GC.Collect will tell garbage collection that managed objects can be collected... this does help, but is not a cure-all. You will find though that .NET will actually collect on managed objects some time later when the app ends or when when there is sufficeint memory pressure to do so (ie the memory needed).

 

7)   When deleting from an array, each pointer in that array must be individually deleted. After this is done, then the array can be deleted. Remember arrays are arrays of pointers.

 

8)   When you have an event which has a com object passed by value and not by reference, you will have a leak if you do not call  Marshal.ReleaseComObject() on that object.  This is true for com managed objects which wrap com objects.  An example of this is Outlook Object Model (OOM) code which passes an item by value to one of its events (such as onEvent_ItemSend()).

 

9) The instance item of a FOREACH loop needs to haveMarshal.ReleaseComObject() called on it if the underlaying object is COM.

 

10) If you use a for loop for looping through a collection which is COM based, you need to call Marshal.ReleaseComObject() on any objects which have references set to anything in that collection.  The same goes for using an enumerator. 

 


转自:http://blogs.msdn.com/b/webdav_101/archive/2008/03/31/detecting-application-memory-leaks-in-unmanaged-c-with-4-lines-of-code.aspx

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值