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.