Detecting Application Memory leaks in Unmanaged C++ with 4 lines of code:
2014-02-19 22:46
1441 查看
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
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
相关文章推荐
- Compile C++ code in Matlab with OpenCV support
- Handle unmanaged code in managed c# with the help of using statement
- .net垃圾回收学习[Best Practices No 5: Detecting .NET application memory leaks][翻译&&学习]]
- Using C++ Code in Android Application
- Detecting memory leaks with HookLogger
- 检查Android本地代码的内存泄漏(Detecting memory leak in Android native code)
- Sinking events from managed code in unmanaged C++
- .net垃圾回收学习【NET Best Practice No: 1:- Detecting High Memory consuming functions in .NET code】【翻译&&学习】
- Tracing memory leaks in .NET applications with ANTS Profiler
- [React] Stop Memory Leaks with componentWillUnmount Lifecycle Method in React
- How to make Office VBA code interact with a VSTO application-level add-in
- Dealing With Memory Leaks in Unreal Engine 4
- Find out C++ Memory Usage in Code
- Detecting .NET application memory leaks
- Debugging Maya Plugin(C++) with CodeBlocks in Linux
- Creating an AVI in memory with C++
- Top 5 common mistakes to cause memory leaks in Symbian C++ applications
- OpenOffice.org Code Snippets--Working with a Presentation Document in C++
- Sinking events from managed code in unmanaged C++
- Compile C++ code in Matlab with OpenCV support