Click here to Skip to main content
15,867,568 members
Articles / Programming Languages / C++

Typical memory leak scenarios analysis: Part 1

Rate me:
Please Sign up or sign in to vote.
1.89/5 (13 votes)
10 Apr 2007CPOL3 min read 39.6K   18   9
This article demonstrates and analyses several typical memory leak scenarios.

Introduction

When a developer investigates memory leak issues, he/she could find the code that allocated the leaked memory with some tools. Most of the time, it is easy to fix. But sometimes, it is still not very clear where the real problem is and how to fix it.

In this article, I will demonstrate some of these scenarios. The analysis and fix will be put at the end of each scenario. All of the problems are from real projects. I have to simplify them due to confidential reasons. You can take a quick test to see whether you can figure out the errors immediately.

1. Destructor

C++
1: class A{ }; 
2: class B : public A { 
3:     private:  
4:         std::string str;  
5:     public: B( std::string& s) {  
6:         str=s; 
7:     }  
8: };  

9: void LeakTest() {  
10:     std::str hello("hello"); 
11:     A * pA = new B(hello);  
12:     if( pA ) { 
13:         delete pA;  
14:    } 
15: }

Error report: You may use different tools to detect a memory leak (the diagram below is generated by Runtimechecker), but the result should be similar to this:

Image 1

There are two panes in the window: the first is the call stack, the second is the source code.

Analysis (destructor is not virtual)

  1. The destructor of class B is not defined, so the compiler will generate one automatically, in which the destructor of std::string will be called for str defined in line 4, and the internal buffer that holds "Hello" will be freed.
  2. The compiler also generates a default destructor for class A, but it is not virtual.
  3. pA is deleted in line 13. Because pA is a pointer of class A and the default destructor of class A is not virtual, only the destructor of class A will be called, and thus the internal buffer allocated by str will be leaked.
  4. You may wonder why the leak was allocated in line 10 instead of line 11. The reason is, std::basic_string (the template of std::string) has some reference counting implementation. You can see it by tracing the code.

Fix: Add a virtual destructor for class A: virtual ~A(){}.

2. Store pointers in container

C++
1: class A{
2:     int a;
3: };

4: class MyContainer{
5: public:
6: std::map<long,A*> <LONG,A*>leakMap; 

7: ~MyContainer(){
8:     std::map<long,A*>::iterator it;
9:     for(it=leakMap.begin();it!=leakMap.end();it++){
10:         if( it->second != NULL)
11:           delete it->second;
12:     }
13: }
14: };

15: MyContainer  con;

16: void LeakTest() {
17:     Test1();
18:     Test2();
19: } 

20: void Test1() {
21:     A* pA1 = new A();
22:     con.leakMap[1000] = pA1; 
23: } 

24: void Test2() {
25:     A* pA2 = new A();
26:     con.leakMap[1000] = pA2; 
27: }

Analysis:

Sometimes it maybe necessary to store pointers in the container. When you replace an existing pointer in a container with a new pointer (in this sample, the operator [] of map in line 26 replaced pA1 with pA2), make sure to delete the old pointer. When the sample puts pA2 into the container in line 26, it should check whether there is a pointer associated with key 1000; if yes, pA1 should be deleted first.

Fix: Add these lines between line 25 and line 26:

C++
std::map<long,A*>::iterator it = con.leakMap.find(1000);
if( it != con.leakMap.end() )
    delete it->second;

This bug looks simple in the above sample. But imagine, if you are working on a big application and Test1() and Test2() are from different source files, it could be hard to find it.

Note: This bug was originally from a server application with more than 150,000 lines of code.

3. void *

C++
1: void ThreadFunc( void * p ) {
2: // Do business logic
3: if( p )
4:    delete p; 
5: }

6:void LeakTest(){
7:     A* p = new A();
8:     _beginthread( ThreadFunc,(void*)p );
9:
}

Analysis:

There is an obvious error; i.e., convert a pointer of class A to void * and delete the void *, and the result is the destructor of class A would not be called and the memory supposed to be freed in the destructor will be leaked. This scenario happens quite often, and the above sample is just one of the variants.

Fix: Change line 4 to: delete (A*)p;.

More samples will be included in Part 2.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Canada Canada
I am a core developer of RuntimeChecker,which detects memory leak for applications developped by visual c++.

Comments and Discussions

 
QuestionA better fix? Pin
Jun Du25-Apr-06 3:51
Jun Du25-Apr-06 3:51 
AnswerRe: A better fix? Pin
Frank _ Li25-Apr-06 6:45
Frank _ Li25-Apr-06 6:45 
GeneralRe: A better fix? Pin
Jun Du25-Apr-06 7:16
Jun Du25-Apr-06 7:16 
GeneralRe: A better fix? Pin
gri10-Apr-07 22:08
gri10-Apr-07 22:08 
GeneralRe: A better fix? Pin
Frank _ Li11-Apr-07 7:55
Frank _ Li11-Apr-07 7:55 
GeneralRedundant NULL check Pin
Johann Gerell24-Apr-06 20:23
Johann Gerell24-Apr-06 20:23 
Jokehi Pin
AnasHashki20-Mar-06 20:54
AnasHashki20-Mar-06 20:54 
GeneralTwo Suggestions Pin
Rick York20-Mar-06 20:26
mveRick York20-Mar-06 20:26 
GeneralRe: Two Suggestions Pin
Frank _ Li21-Mar-06 10:32
Frank _ Li21-Mar-06 10:32 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.