Ant*_*eru 30 c++ debugging memory-management
如何跟踪C++中的内存分配,尤其是new
/ delete
.对于一个对象,我可以轻松覆盖operator new
,但我不确定如何全局覆盖所有分配,以便它们通过我的自定义new
/ delete
.这应该不是一个大问题,但我不确定应该怎么做(#define new MY_NEW
?).
一旦这个工作,我会认为它足以在分配的指针/位置的某处有一个映射,所以我可以跟踪当前"活动"的所有分配,并且 - 在应用程序结束时 - 检查分配没有被释放的.
好吧,这似乎再次像肯定已经多次完成了几次,所以任何好的库(最好是便携式的)?
Joh*_*itb 26
我建议你使用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
}
Run Code Online (Sandbox Code Playgroud)
我们必须为我们的map使用我们自己的allocator,因为标准的将使用我们重写的operator new,这将导致无限递归.
确保如果覆盖operator new,则使用映射注册分配.删除由新的放置形式分配的内存也将使用该删除操作符,因此如果您不知道的某些代码重载了不使用您的地图的操作符,则会变得棘手,因为操作符删除会告诉您它没有被分配和使用std::free
释放内存.
另请注意,正如Pax也指出了他的解决方案,这只会显示由使用我们自己定义的运算符new/delete的代码引起的泄漏.因此,如果您想使用它们,请将它们的声明放在标题中,并将其包含在应该观看的所有文件中.
Nec*_*cro 26
具体来说,使用valgrind的massif工具.与memcheck相反,massif不关心非法使用内存,而是跟踪分配随着时间的推移.它可以很好地"有效"地测量程序的堆内存使用情况.最好的部分是,您不必编写任何代码.尝试:
http://valgrind.org/docs/manual/ms-manual.html
或者,如果你真的不耐烦:
valgrind --tool=massif <executable> <args>
ms_print massif.out.<pid> | less
Run Code Online (Sandbox Code Playgroud)
这将为您提供一段时间内的分配图,以及返回大分配所在位置的跟踪图.这个工具最好在Linux上运行,我不知道是否有Windows varient.它不会在OS X上运行
祝好运!
pax*_*blo 10
您可以使用http://www.flipcode.com/archives/How_To_Find_Memory_Leaks.shtml上的代码进行以下修改:仅当您有一个大的honkin'源文件时,给出的代码才有效.我把这个问题排在另外一个关于SO的问题(这里).
首先,不要更改stdafx.h,在自己的文件中进行修改.
例如,创建一个单独的头文件mymemory.h并将您的函数原型放入其中(请注意,这没有正文):
inline void * __cdecl operator new(unsigned int size,
const char *file, int line);
Run Code Online (Sandbox Code Playgroud)
同样在该头文件中,将其他原型放入AddTrack(),DumpUnfreed()等,以及#define,typedef和extern语句:
extern AllocList *allocList;
Run Code Online (Sandbox Code Playgroud)
然后,在一个新的mymemory.cpp(也包括#include的mymemory.h)中,将allocList的实际定义与所有实际函数(不仅仅是原型)放在一起,并将该文件添加到项目中.
然后,#include "mymemory.h"
在每个需要跟踪内存的源文件中(可能都是它们).因为头文件中没有定义,所以在链接期间不会出现重复,并且因为声明存在,所以也不会得到未定义的引用.
请记住,这不会跟踪您不编译的代码中的内存泄漏(例如,第三方库),但它应该让您了解自己的问题.
好吧,您可以重新实现全局运算符new和delete以提供您想要的功能,但我建议不要这样做,除非这是跟踪内存分配的唯一方法,例如由于您的平台的限制.
内存调试程序可用于大多数常见开发平台.看看PurifyPlus是否适用于Windows和各种Unix或valgrind的商业解决方案,适用于在Linux上运行的开源软件(可能还有其他操作系统,但我只在Linux上使用它).
如果您打算更换全局运算符,请查看本文.
归档时间: |
|
查看次数: |
31519 次 |
最近记录: |