I recently discovered the following memory leak in my openmp code.
When a class it's destructor is a virtual (due to inheritance) and an object of that class type is used as a first private, then the destructor is not called.
Is this behavior normal, as far as I know, I don't think so.
Here is a very simple example code.
- Code: Select all
#include <iostream>
class array {
public :
array(void) : ptr(NULL), l(0){ };
array(const array &c): l(c.l) {
ptr = new double [l];
std::cout << "copy construct pointer : " << ptr << std::endl;
};
array(size_t L): l(L) {
ptr = new double [l];
std::cout << "construct pointer : " << ptr << std::endl;
}
virtual ~array(void) {
std::cout << "destroy ptr : " << ptr << std::endl;
delete [] ptr;
}
private :
double *ptr;
size_t l;
};
int main(void) {
array tmp(100);
#pragma omp parallel for firstprivate(tmp1)
for (int a = 0; a >= 0; --a) {
}
return 0;
}
Below you find the compilation and valgrind output (mark that tmp has a size of 1600 bytes). The problem is ran on a dual core system.
- Code: Select all
[ openmp]$ g++ -ggdb --openmp firstprivate.cpp
[ openmp]$ ./a.out
construct pointer : 0x602010
copy construct pointer : 0x602540
copy construct pointer : 0x602870
destroy ptr : 0x602010
[ openmp]$ valgrind --leak-check=full --show-reachable=yes ./a.out
==7319== Memcheck, a memory error detector.
==7319== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et al.
==7319== Using LibVEX rev 1804, a library for dynamic binary translation.
==7319== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==7319== Using valgrind-3.3.0, a dynamic binary instrumentation framework.
==7319== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et al.
==7319== For more details, rerun with: -v
==7319==
construct pointer : 0x5f35298
copy construct pointer : 0x5f35878
copy construct pointer : 0x5f35bc8
destroy ptr : 0x5f35298
==7319==
==7319== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 3 from 1)
==7319== malloc/free: in use at exit: 1,928 bytes in 4 blocks.
==7319== malloc/free: 8 allocs, 4 frees, 3,432 bytes allocated.
==7319== For counts of detected errors, rerun with: -v
==7319== searching for pointers to 4 not-freed blocks.
==7319== checked 8,585,888 bytes.
==7319==
==7319== 24 bytes in 1 blocks are still reachable in loss record 1 of 3
==7319== at 0x4C1ED1B: malloc (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==7319== by 0x4C1EE64: realloc (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==7319== by 0x53AB288: (within /usr/lib64/libgomp.so.1.0.0)
==7319== by 0x53AD4A0: (within /usr/lib64/libgomp.so.1.0.0)
==7319== by 0x400D41: main (firstprivate.cpp:26)
==7319==
==7319==
==7319== 304 bytes in 1 blocks are possibly lost in loss record 2 of 3
==7319== at 0x4C1DE2C: calloc (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==7319== by 0x400FE62: _dl_allocate_tls (dl-tls.c:300)
==7319== by 0x57C3B41: pthread_create@@GLIBC_2.2.5 (in /lib64/libpthread-2.7.so)
==7319== by 0x53AD3BE: (within /usr/lib64/libgomp.so.1.0.0)
==7319== by 0x400D41: main (firstprivate.cpp:26)
==7319==
==7319==
==7319== 1,600 bytes in 2 blocks are definitely lost in loss record 3 of 3
==7319== at 0x4C1F1F7: operator new[](unsigned long) (in /usr/lib64/valgrind/amd64-linux/vgpreload_memcheck.so)
==7319== by 0x400EF7: array::array(array const&) (firstprivate.cpp:7)
==7319== by 0x400D84: main.omp_fn.0 (firstprivate.cpp:28)
==7319== by 0x400D4A: main (firstprivate.cpp:26)
==7319==
==7319== LEAK SUMMARY:
==7319== definitely lost: 1,600 bytes in 2 blocks.
==7319== possibly lost: 304 bytes in 1 blocks.
==7319== still reachable: 24 bytes in 1 blocks.
==7319== suppressed: 0 bytes in 0 blocks.
When removing the virtual tag from the destructor, the leak vanishes and the output makes more sense.
- Code: Select all
[ openmp]$ ./a.out
construct pointer : 0x602010
copy construct pointer : 0x602540
destroy ptr : 0x602540
copy construct pointer : 0x602540
destroy ptr : 0x602540
destroy ptr : 0x602010
The two leaks of 24 and 304 bytes are probably due to a bug in g++ (see viewtopic.php?f=3&t=128).
Is the behavior addressed above correct or is it again a bug?
