如何跟踪C ++中的内存分配,尤其是new
/ delete
完成的内存分配。对于某个对象,我可以轻松覆盖operator new
,但我不确定如何全局覆盖所有分配,以便它们通过我的自定义new
/ delete
。这应该不是一个大问题,但我不确定应该怎么做(#define new MY_NEW
?)。
一旦这个工作,我会认为它足以在分配的指针/位置的某处有一个地图,所以我可以跟踪当前“活动”的所有分配和 - 在应用程序结束时 - 检查尚未释放的分配。
嗯,这看起来好像至少已经好几次了,所以任何好的库(最好是便携式的)?
答案 0 :(得分:26)
我建议你使用valgrind
用于linux。它会捕获未释放的内存,以及写入未分配内存等其他错误。另一种选择是mudflap,它告诉你没有释放内存。在gcc中使用-fmudflap -lmudflap
选项,然后使用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,这将导致无限递归。
确保如果覆盖operator new,则使用地图注册分配。删除由新的放置形式分配的内存也将使用该删除操作符,因此如果您不知道的某些代码重载了不使用您的地图的操作符,它会变得棘手,因为操作符删除会告诉您它没有被分配和使用std::free
释放内存。
另请注意,正如 Pax 也指出了他的解决方案,这只会显示由使用我们自己定义的运算符new / delete的代码引起的泄漏。因此,如果您想使用它们,请将它们的声明放在标题中,并将其包含在应该观看的所有文件中。
答案 1 :(得分:24)
具体来说,请使用valgrind的massif工具。与memcheck相反,massif不关心非法使用内存,而是跟踪分配随着时间的推移。它可以很好地“有效”地测量程序的堆内存使用情况。最好的部分是,您不必编写任何代码。尝试:
http://valgrind.org/docs/manual/ms-manual.html
或者如果你真的不耐烦:
valgrind --tool=massif <executable> <args>
ms_print massif.out.<pid> | less
这将为您提供一段时间内的分配图表,并返回显示大量分配的位置的图表。这个工具最好在Linux上运行,我不知道是否有Windows varient。 在OS X上工作。
祝你好运!答案 2 :(得分:9)
您可以使用http://www.flipcode.com/archives/How_To_Find_Memory_Leaks.shtml处的代码进行以下修改:仅当您有一个大的honkin'源文件时,给定的代码才有效。我在SO(here)上另外提出了一个问题。
首先,不更改stdafx.h,在您自己的文件中进行修改。
创建一个单独的头文件mymemory.h并将您的函数原型放入其中(例如,请注意,它没有 body ):
inline void * __cdecl operator new(unsigned int size,
const char *file, int line);
同样在该标题中,将其他原型放入AddTrack(),DumpUnfreed()等,以及#define,typedef和extern语句:
extern AllocList *allocList;
然后,在一个新的mymemory.cpp(也包括#include的mymemory.h)中,将allocList的实际定义与所有实际函数(不仅仅是原型)放在一起,并将该文件添加到项目中。
然后,#include "mymemory.h"
在您需要跟踪内存的每个源文件中(可能都是它们)。因为头文件中没有定义,所以在链接期间不会出现重复,并且因为声明存在,所以也不会得到未定义的引用。
请记住,这不会跟踪您未编译的代码中的内存泄漏(例如,第三方库),但它应该让您了解自己的问题。
答案 3 :(得分:7)
好吧,你可以重新实现全局运算符new和delete来为你提供你想要的功能,但是我建议不要这样做,除非这是跟踪内存分配的唯一方法,例如由于你的平台的限制
内存调试程序适用于大多数常见开发平台。查看PurifyPlus以获取适用于Windows和各种Unix的商业解决方案,或valgrind查看适用于Linux(可能还有其他操作系统但我只使用过它的开源源代码)的商业解决方案的Linux)。
如果您打算更换全局运营商,请查看this article。
答案 4 :(得分:3)
对于我们的Windows平台C ++项目,我使用VLD,Visual Leak Detector,它几乎太容易实现,可以在您的应用程序退出时跟踪和报告内存泄漏 - 最重要的是免费且源代码可用。系统可以设置为以多种方式报告(磁盘记录器,IDE,XML等),并且对于检测Windows服务中的泄漏非常有价值,而这些泄漏始终是调试的挑战。因此,当您正在寻找便携式解决方案时,如果您想自己动手,您当然可以查看指导来源。希望它有所帮助。
引用该网站:
这是一种非常有效的快速方法 诊断并修复内存泄漏 C / C ++应用程序。
答案 5 :(得分:3)
在Linux上,至少有两种传统方法:
因此,您不仅可以捕获new和delete,还可以捕获C风格的内存分配函数。我还没有在Windows上做过这个,但我已经看到了重写DLL如何链接的方法(虽然我记得它们有点笨拙)。
但请注意,除了这些是有趣的技术之外,我建议使用valgrind做你想做的事情。
答案 6 :(得分:3)
如果您在Windows下开发,免费工具DebugDiag将帮助查找内存并处理泄漏。
您无需为DebugDiag工作提供程序。
虽然它不是最简单或最直观的程序!请确保您获取有关如何使用它的教程和说明。
答案 7 :(得分:1)
不是直接回答你的问题,但是如果你真的只想在程序结束时获得泄漏的堆对象列表,那么你可以用valgrind运行该程序。
对于MS VS,您可以使用the Debug CRT Heap。不像valgrind那么简单,这里有点太多解释,但可能会做你想要的。
答案 8 :(得分:1)
答案 9 :(得分:0)
#include<iostream>
void * operator new(size_t size)
{
std::cout<<"Allocating:"<<size<<std::endl;
return malloc (size);
}
void operator delete(void *ptr)
{
std::cout<<"Deleting:"<<ptr<<std::endl;
free(ptr);
}
int main() {
std::string ss("1234567890123456");
}
如果你没有看到被称为 , 的重载运算符(你可能在不同的编译器上,然后我的 g++)尝试增加字符串的长度。
答案 10 :(得分:0)
检查这个非常方便的代码,而不是new
使用NEW
并跟踪NewHelper
构造函数中的所有分配:
#include <iostream>
class NewHelper
{
private :
void* addr = nullptr;
public :
NewHelper(void * addr_)
{
addr = addr_;
std::cout<<addr<<std::endl;
}
template <class T>
operator T ()
{
return (T)addr;
}
};
#define NEW (NewHelper)(void*)new
int main()
{
int * i = NEW int(0);
return 0;
}
答案 11 :(得分:0)
如果我需要一个工具,我通常从我的编译器/标准库提供的开始。
答案 12 :(得分:0)
您可以使用此 MemTracker.h 中的头文件( link )添加到您的解决方案中,以跟踪C中的内存分配/释放和C ++。它显示您是否有内存泄漏以及哪一行代码负责它。
答案 13 :(得分:0)
它并不便宜,但我曾经在我的C ++时代发现purify是调试泄漏和其他内存问题的最佳工具(同样它现在由IBM拥有,所以surport下山)。 Bounds Checker被某些人所喜欢,但对我正在开发的软件效果不佳。
答案 14 :(得分:0)
我注意到很多其他答案都集中在你可以使用的工具上。我已经使用了其中一些,而且它们有很多帮助。
但作为编程练习,并且看到你使用c ++,你需要覆盖全局new和delete,以及malloc,free和realloc。您认为只覆盖new和delete就足够了,但是std :: string和其他类可能会使用malloc,尤其是realloc。
然后,一旦你有了这个,你就可以开始添加标题来检查内存覆盖,记录每个分配的堆栈跟踪等等。
总而言之,我建议您使用此处提到的工具之一,但编写自己的系统可能会很有趣。
答案 15 :(得分:0)
如果你是在linux下开发的,那么最好的工具之一(例如检测内存泄漏,跟踪在某些代码位置完成的分配)是valgrind,特别是它的massif工具。唯一的缺点是程序运行速度较慢(或速度较慢),因此它只对调试有用。
答案 16 :(得分:-1)
如果您打算将此作为编程练习,那么它可能会让您更深入地编写自己的智能指针类,并在整个项目(或项目模块)中始终使用它们。 / p>