开发者

Fighting Memory Leaks

Memory开发者_开发技巧 leaks are a nightmare. I know: I have some.

What is the most effective (least painful and time consuming) way to locate them?

I use Visual Studio 2010 and develop in C++ for Windows.


Smart Pointers is probably what you are looking for. At least when allocating objects.

For containers, use the STL where possible.

If you are looking for existing memory leaks, you can use DEBUG_NEW if you are in VS2010.


I've used Valgrind for Linux, but you can find few useful links here Is there a good Valgrind substitute for Windows?


if your code currently has a lot of calls to new and delete, and you can use boost (or tr1), the following types will be immensely helpful in fighting leaks

  • boost::shared_ptr
  • boost::scoped_ptr
  • boost::shared_array
  • boost::scoped_array

replace boost with std::tr1 if you are unable to use boost. More documentation is available on the boost website.


for MSVC I use following technique:

  1. use precompiled headers (e.g. stdafx.h) at the very beginning of stdafx.h put:

    #if defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER)
        #define _CRTDBG_MAP_ALLOC
        #include <crtdbg.h>
        #define DEBUG_NEW new( _NORMAL_BLOCK, __FILE__, __LINE__)
    #endif
    
  2. then include all headers you aren't going to modify (or very rarely at least). the point here is to include headers with overloaded operator new

  3. at the end of stdafx.h put:

    #if defined(_WIN32) && defined(_DEBUG) && defined(_MSC_VER)
        #if defined(new)
            #undef new
        #endif
        #define new DEBUG_NEW
    #endif
    
  4. in your main() put

    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
    

    as the first line.

test:

int main()
{
    _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF/* | _CRTDBG_CHECK_CRT_DF*/);
     int* i = new int();
     std::vector<int>* v =  new std::vector<int>(1000);
     return 0;
}

run this under debugger. at VC output window you'll find something like:

The thread 'Win32 Thread' (0x1b1c) has exited with code 0 (0x0).
Detected memory leaks!
Dumping objects ->
{97} normal block at 0x02725E38, 4000 bytes long.
 Data: <                > 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
c:\main.cpp(7) : {96} normal block at 0x02725DE8, 20 bytes long.
 Data: <        8^r  mr > 00 00 00 00 CD CD CD CD 38 5E 72 02 D8 6D 72 02 
c:\main.cpp(6) : {95} normal block at 0x02725DA8, 4 bytes long.
 Data: <    > 00 00 00 00 
Object dump complete.
The program '[3756] test.exe: Native' has exited with code 0 (0x0).

as you can see, there're 3 memleaks: two in main() and one somewhere else. "somewhere else" is not detected because it's in <vector> and it was included before our #define new DEBUG_NEW. this report is convenient enough because after every run in debugger you see if new memleaks appeared, and you can just doubl click on corresponding memleak to go to exact place in the editor. {97}, {96} etc. numbers in the example indicate the sequence number of memory allocation, you can use it to trigger corresponding memory allocation by putting _CrtSetBreakAlloc(memalloc_seq_no); before this allocation actually happens.

the technique is far from be perfect and universal, but I found it very useful in everyday programming in VC.

more info in MSDN


Before going the debug road, let's us consider the design.

There are two cardinal rules to avoid memory leaks:

  1. don't use new
  2. don't use delete

This might seem surprising at first :)

1. Don't use new

You should not allocate on the heap whenever you can actually avoid it:

  • allocate on the stack
  • use containers that will handle the memory allocations for you

Each new you remove from your code, is necessarily a memory leak you won't have.

2. Don't use delete

The problem is that matching each new with a delete is extremely difficult. It's difficult to remember to delete it, it's difficult not to delete it twice.

C++ offers a handy solution in the form of SBRM (Scoped-Bound Resources Management, also know by the RAII acronym whose name is rather misleading). This notably means smart managers.

Whenever you actually call new, make sure to immediately pass on the responsability of managing the newly allocated object to either a smart pointer, or some other equally smart container (like boost::ptr_vector for example).

3. Code reviews

  • Question every use of new
  • Make sure that every new result is immediately placed within a smart manager

Note: the only place for delete is when YOU are writing a smart manager. There is normally little point in doing so, given the wide selection available. If you do, however, remember that it's guru area (exception safety, ...); the one advice I can give, is that a smart manager does only one thing: it manages smartly a SINGLE resource. Try to combine it with something else, and you'll fail, and you'll not even realize it


I'm going through my program and reducing the use of smart pointers, as well as singletons.

The idea is to use pointers and smart pointers only when necessary.

I have cleaned up a lot of memory leaks by using these two techniques. For example, if a class needs access to a singleton, convert the singleton to a regular object and pass as an argument. This has as side effect of showing the true requirements of the methods and class rather than having hidden singleton requirements.


If you can reproduce the issue I'd recommend using umdh from the windows debugging tools package. If you turn on user mode stack traces using gflags or application verifier for your executable it can take before and after snapshots of all your memory allocations which can then be used to track them down. You can also attach with windbg and use the !heap -l to list any non referenced allocations and can print them using !heap -p -a .

You could also use static analysis tools like Coverity (not sure if there are any good free alternatives) but it might not catch every situation.


Good plan for world domination with the memory leaks is to not use heap at all.

Instead of:

class A { A() { m_a = new int; } ~A() { delete m_a; } int *m_a; };

can always use:

class A { int m_a; };

and it's considerably simpler and more convinient.

It also works in other situations, Instead of:

MyObject *create_obj(int a) { 
   switch(a) { 
      case 0: return new MyDerived1; 
      case 1: return new MyDerived2; 
   };
}
int main() { MyObject *o = create_obj(1); o->do_x(); o->do_y(); delete o; }

Could always use this:

class Obj {
   Obj(int a) : a(a) { }
   void do_x() { switch(a) { case 0: ... break; case 1: ... break; }; }
   void do_y() { switch(a) { case 0: ... break; case 1: ... break; }; }
   int a;
}; 
int main() { Obj o(1); o.do_x(); o.do_y(); }

Copying a large data structure from scope to another for stack allocation works like this:

void Collect(MyStorage &s)
{
   MyObject o;
   MyObject2 o2(o);
   MyObject3 o3(o2);
   s.Copy(o3);
}
void f(const MyStorage &s);
int main() { MyStorage s; Collect(s); f(s); }

This way creating large data structure on stack will work fine. The Storage class needs heap.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜