Introduction
In earlier blog, Debugging and Fixing Mem Leaks β Part 1 β My sky (freewindcode.com), I explained about Direct and Indirect memory leak.
Various causes of memory leaks were discussed:
- failure to create a separate event to handle deallocating memory
- mem leak due to reassigning an argument pointer inside a function and failing to deallocate the original heap mem when the function exits.
- failure to create virtual destructor.
- forget to deregister an observer in Observer pattern design. (also called lapse listener problem)
In this blog, I discuss about another cause:
- failure to deallocate member pointer in class destructor.
In other words, it is failure to deallocate resources in wrapper class. Depending on the scenario, we may have Direct or Indirect Leaks.
Direct Mem Leak by failing to deallocate resources in Wrapper class
Wrapper class is any class that hold resources, such as a pointer to some memory locations. Wrapper class may allocate the resources sometime during its life, such as in its constructor.
If the wrapper object is deallocated, we need to explicitly deallocate the memory location at the resource that it holds; otherwise, the resource is deleted, but the memory location is not free up. Direct leak will occur.
The following program illustrates such scenarios:
#include <iostream>
using namespace std;
class Resource {
public:
Resource() { cout<<"Resource constructor"<<endl; }
~Resource() { cout<<"Resource destructor"<<endl; }
};
class MyWrapperClass {\
public:
MyWrapperClass() {
cout<<"MyWrapperClass constructor"<<endl;
res_ = new Resource();
}
~MyWrapperClass(){
cout<<"MyWrapperClass destructor" <<endl;
// fail to delete res_
}
private:
Resource * res_;
};
int main(){
cout<<"Size of Resouce: "<<sizeof(Resource)<<endl;
MyWrapperClass * cl = new MyWrapperClass();
delete cl;
return 0;
}
Console log:
Resource pointer res_was created and memory was allocated at line 15.- However, when
MyWrapperClassis deleted, the destructor doesn't call to deallocate the memory location pointed byres_pointer. - While the pointer
res_is removed, the memory location pointed byresis not deallocated. This causes direct leak. The size of the direct leak is the same as the size ofResource.
Size of Resouce: 1
MyWrapperClass constructor
Resource constructor
MyWrapperClass destructor
Program stderr
=================================================================
==1==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 1 byte(s) in 1 object(s) allocated from:
#0 0x555a6fd4e1cd in operator new(unsigned long) /root/llvm-project/compiler-rt/lib/asan/asan_new_delete.cpp:95:3
#1 0x555a6fd5059f in MyWrapperClass::MyWrapperClass() /app/example.cpp:15:16
#2 0x555a6fd50506 in main /app/example.cpp:27:31
#3 0x7f2e0af5f082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)
SUMMARY: AddressSanitizer: 1 byte(s) leaked in 1 allocation(s)
Indirect leak in wrapper class
The following illustrates a scenario where indirect leak occurs.
MyClasspointer,math, is created, and memory is allocated at where it points to.- The pointer is then reassigned, it is pointed to another location, which is allocated. (Yeah, I changed class a lot). So we have 2 memory locations now.
MyClassitself has a private pointer,resource_, typeResource. Thus, for each memory location that was created, there is another section of memory that corresponds toResourceand is accessible by the private pointerresource_;- When
MyClasspointer,math, is deleted, only the later memory location is deleted. - The memory section that
resource_points to, in the earlier memory location section is not deleted, thus it is considered indirect leak.
#include <iostream>
using namespace std;
typedef struct Resource{
int fee; // 4 bytes
};
class MyClass {
public:
MyClass( string teacher, Resource * res ) :
teacher_(teacher),
res_(res) { }
~MyClass() {
// delete res_; // cause indirect leak
}
private:
string teacher_;
Resource * res_;
};
int main(){
cout<<"size of My Class "<<sizeof(MyClass)<<endl;
cout<<"size of Resource "<<sizeof(Resource)<<endl;
Resource * res = new Resource;
res->fee=100;
MyClass * cl = new MyClass("Tom", res);
cl = new MyClass ("Joh", res);
delete cl;
}
Error log:
- the direct leak is the first memory location that was allocated in line 23.
- the indirect leak is the encapsulating memory area within the first memory location that could be accessible by pointer
res, which was created in line 21 - Note that
Resourcehas size 8 bytes, and the indirect leak size is the same. - The MyClass size is 40 bytes, and direct leak size is also 40 bytes
size of My Class 40
size of Resource 4
Program stderr
=================================================================
==1==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 40 byte(s) in 1 object(s) allocated from:
#0 0x7f7b12086518 in operator new(unsigned long) (/opt/compiler-explorer/gcc-13.2.0/lib64/libasan.so.8+0xdb518) (BuildId: 5ce9c09d3612315d01d50bcaafaea176e7ddab77)
#1 0x40246e in main /app/example.cpp:26
#2 0x7f7b11a04082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)
Indirect leak of 4 byte(s) in 1 object(s) allocated from:
#0 0x7f7b12086518 in operator new(unsigned long) (/opt/compiler-explorer/gcc-13.2.0/lib64/libasan.so.8+0xdb518) (BuildId: 5ce9c09d3612315d01d50bcaafaea176e7ddab77)
#1 0x402416 in main /app/example.cpp:24
#2 0x7f7b11a04082 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x24082) (BuildId: 1878e6b475720c7c51969e69ab2d276fae6d1dee)
SUMMARY: AddressSanitizer: 44 byte(s) leaked in 2 allocation(s).