我建议你使用valgrind
对于Linux。它会捕获未释放的内存,以及其他错误,例如写入未分配的内存。另一个选项是 mudflap,它也会告诉您未释放的内存。使用-fmudflap -lmudflap
gcc 的选项,然后启动你的程序MUDFLAP_OPTIONS=-print-leaks ./my_program
.
这是一些非常简单的代码。它不适合复杂的跟踪,但旨在向您展示如果您自己实现的话,原则上您将如何做到这一点。像这样的东西(省略了调用注册的 new_handler 和其他详细信息的内容)。
template<typename T>
struct track_alloc : std::allocator<T> {
typedef typename std::allocator<T>::pointer pointer;
typedef typename std::allocator<T>::size_type size_type;
template<typename U>
struct rebind {
typedef track_alloc<U> other;
};
track_alloc() {}
template<typename U>
track_alloc(track_alloc<U> const& u)
:std::allocator<T>(u) {}
pointer allocate(size_type size,
std::allocator<void>::const_pointer = 0) {
void * p = std::malloc(size * sizeof(T));
if(p == 0) {
throw std::bad_alloc();
}
return static_cast<pointer>(p);
}
void deallocate(pointer p, size_type) {
std::free(p);
}
};
typedef std::map< void*, std::size_t, std::less<void*>,
track_alloc< std::pair<void* const, std::size_t> > > track_type;
struct track_printer {
track_type * track;
track_printer(track_type * track):track(track) {}
~track_printer() {
track_type::const_iterator it = track->begin();
while(it != track->end()) {
std::cerr << "TRACK: leaked at " << it->first << ", "
<< it->second << " bytes\n";
++it;
}
}
};
track_type * get_map() {
// don't use normal new to avoid infinite recursion.
static track_type * track = new (std::malloc(sizeof *track))
track_type;
static track_printer printer(track);
return track;
}
void * operator new(std::size_t size) throw(std::bad_alloc) {
// we are required to return non-null
void * mem = std::malloc(size == 0 ? 1 : size);
if(mem == 0) {
throw std::bad_alloc();
}
(*get_map())[mem] = size;
return mem;
}
void operator delete(void * mem) throw() {
if(get_map()->erase(mem) == 0) {
// this indicates a serious bug
std::cerr << "bug: memory at "
<< mem << " wasn't allocated by us\n";
}
std::free(mem);
}
int main() {
std::string *s = new std::string;
// will print something like: TRACK: leaked at 0x9564008, 4 bytes
}
我们必须为我们的映射使用我们自己的分配器,因为标准分配器将使用我们重写的运算符 new,这将导致无限递归。
确保如果您覆盖 new 运算符,则使用映射来注册您的分配。删除通过 new 的放置形式分配的内存也将使用该删除运算符,因此如果您不知道的某些代码重载了不使用您的映射的运算符 new ,它可能会变得很棘手,因为运算符删除会告诉您它尚未分配并且使用std::free
来释放内存。
另请注意,如Pax他也指出了他的解决方案,这只会显示由使用我们自己定义的运算符 new/delete 的代码引起的泄漏。因此,如果您想使用它们,请将它们的声明放在标头中,并将其包含在所有应该监视的文件中。