当前位置:  开发笔记 > 编程语言 > 正文

如何在C++中跟踪内存分配(尤其是新建/删除)

如何解决《如何在C++中跟踪内存分配(尤其是新建/删除)》经验,为你挑选了4个好方法。

如何跟踪C++中的内存分配,尤其是new/ delete.对于一个对象,我可以轻松覆盖operator new,但我不确定如何全局覆盖所有分配,以便它们通过我的自定义new/ delete.这应该不是一个大问题,但我不确定应该怎么做(#define new MY_NEW?).

一旦这个工作,我会认为它足以在分配的指针/位置的某处有一个映射,所以我可以跟踪当前"活动"的所有分配,并且 - 在应用程序结束时 - 检查分配没有被释放的.

好吧,这似乎再次像肯定已经多次完成了几次,所以任何好的库(最好是便携式的)?



1> Johannes Sch..:

我建议你使用valgrindlinux.它会捕获未释放的内存,以及写入未分配内存等其他错误.另一种选择是mudflap,它告诉你没有释放内存.使用-fmudflap -lmudflapgcc选项,然后启动你的程序MUDFLAP_OPTIONS=-print-leaks ./my_program.

这是一些非常简单的代码.它不适合复杂的跟踪,但是如果你自己实现它,它的目的是向你展示你原则上如何做.像这样的东西(省略了调用注册的new_handler和其他细节的东西).

template
struct track_alloc : std::allocator {
    typedef typename std::allocator::pointer pointer;
    typedef typename std::allocator::size_type size_type;

    template
    struct rebind {
        typedef track_alloc other;
    };

    track_alloc() {}

    template
    track_alloc(track_alloc const& u)
        :std::allocator(u) {}

    pointer allocate(size_type size, 
                     std::allocator::const_pointer = 0) {
        void * p = std::malloc(size * sizeof(T));
        if(p == 0) {
            throw std::bad_alloc();
        }
        return static_cast(p);
    }

    void deallocate(pointer p, size_type) {
        std::free(p);
    }
};

typedef std::map< void*, std::size_t, std::less, 
                  track_alloc< std::pair > > 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
}

我们必须为我们的map使用我们自己的allocator,因为标准的将使用我们重写的operator new,这将导致无限递归.

确保如果覆盖operator new,则使用映射注册分配.删除由新的放置形式分配的内存也将使用该删除操作符,因此如果您不知道的某些代码重载了不使用您的地图的操作符,则会变得棘手,因为操作符删除会告诉您它没有被分配和使用std::free释放内存.

另请注意,正如Pax也指出了他的解决方案,这只会显示由使用我们自己定义的运算符new/delete的代码引起的泄漏.因此,如果您想使用它们,请将它们的声明放在标题中,并将其包含在应该观看的所有文件中.



2> Necro..:

具体来说,使用valgrind的massif工具.与memcheck相反,massif不关心非法使用内存,而是跟踪分配随着时间的推移.它可以很好地"有效"地测量程序的堆内存使用情况.最好的部分是,您不必编写任何代码.尝试:

http://valgrind.org/docs/manual/ms-manual.html

或者,如果你真的不耐烦:

valgrind --tool=massif  
ms_print massif.out. | less

这将为您提供一段时间内的分配图,以及返回大分配所在位置的跟踪图.这个工具最好在Linux上运行,我不知道是否有Windows varient.它不会在OS X上运行

祝好运!



3> paxdiablo..:

您可以使用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);

同样在该头文件中,将其他原型放入AddTrack(),DumpUnfreed()等,以及#define,typedef和extern语句:

extern AllocList *allocList;

然后,在一个新的mymemory.cpp(也包括#include的mymemory.h)中,将allocList的实际定义与所有实际函数(不仅仅是原型)放在一起,并将该文件添加到项目中.

然后,#include "mymemory.h"在每个需要跟踪内存的源文件中(可能都是它们).因为头文件中没有定义,所以在链接期间不会出现重复,并且因为声明存在,所以也不会得到未定义的引用.

请记住,这不会跟踪您不编译的代码中的内存泄漏(例如,第三方库),但它应该让您了解自己的问题.



4> Timo Geusch..:

好吧,您可以重新实现全局运算符new和delete以提供您想要的功能,但我建议不要这样做,除非这是跟踪内存分配的唯一方法,例如由于您的平台的限制.

内存调试程序可用于大多数常见开发平台.看看PurifyPlus是否适用于Windows和各种Unix或valgrind的商业解决方案,适用于在Linux上运行的开源软件(可能还有其他操作系统,但我只在Linux上使用它).

如果您打算更换全局运算符,请查看本文.

推荐阅读
wangtao
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有