诡异的内存泄露

诡异问题

最近项目中遇到了诡异的内存泄露,最开始用肉眼看,看malloc realloc和free是否对应上了,new和delete是否对应上了,无果。 再怀疑是tcmalloc搞的鬼,用heap check追踪了一阵,无果,主要是项目太大(跑起来占几十GB内存、70多线程),启动起来都要半分钟, 所以调试起来非常慢,尽管heap check比valgrind快多了,但还是不爽。 接着,编译时就把tcmalloc干掉了,结果还是泄露,看样子tcmalloc是无辜的,再怀疑是-O3的问题, 把-O3去掉,还是泄露,最终还是得从自己的代码里找问题,结果发现更诡异的现象:有一个析构函数,写在头文件里,内存就泄露, 写在cpp文件里,内存就不泄露。

问题原因

最终在大量的编译时产生的warning信息中找到了答案:对于非完整类型(incomplete type),不能使用delete来析构,具体见下面的举例说明。

首先举例有如下几个简单的代码文件:

//文件b.h
#ifndef __B_H__
#define __B_H__

#include <iostream>
class B
{
public:
    B()
    {
        std::cout << "b constructor" << std::endl;
    }
    ~B()
    {
        std::cout << "b destructor" << std::endl;
    }
};

#endif
//文件a.h
#ifndef __A_H__
#define __A_H__

#include <iostream>

//类声明
class B;

class A
{
public:
    A();
    ~A()
    {
        if (_one) {
            delete _one;
            _one = NULL;
        }
    }

private:
    B *_one;
};

#endif
//文件a.cpp
#include "a.h"
#include "b.h"

A::A()
{
    _one = new B;
}
//文件main.cpp
#include <iostream>
#include "a.h"
using namespace std;

int main()
{
    A a;
    return 0;
}

编译

g++ a.cpp main.cpp -o run -I./

时会报如下warning:

In file included from a.cpp:1:
a.h: In destructor ‘A::~A()’:
a.h:17: warning: possible problem detected in invocation of delete operator:
a.h:17: warning: invalid use of incomplete type ‘struct B’
a.h:6: warning: forward declaration of ‘struct B’
a.h:17: note: neither the destructor nor the class-specific operator delete will be called, even if they are declared when the class is defined.

在a.h文件中,类型B就是所谓的非完整(incomplete type)类型,只是用class B;进行了声明, 而并没有include "b.h"包含具体头文件,这种做法是值得推荐的,因为a.h中只有B*指针类型, 只声明一下class B;可以加快编译,避免b.h被编译器反复读取。

在此种情形下,delete非完整类型就不被支持,正如编译器的warning所言:类的析构函数和操作符不会被调用, 换句话说,就什么都不会做。但这只是一个warning,并不是error,所以程序还是被正常编译生成。

一个大模块往往由很多人共同开发,各种风格代码,各种接口定义习惯,关键是编译出现的warning谁也不愿意管,warning一多,有用的warning就被忽略了。如此就出现了上面诡异的现象:写在cpp文件中不泄露,而写在头文件中就泄露。

教训

编译报的warning一定得解决,一来编译时心情舒畅,二来便于扼杀问题于摇篮。 除内联和模板等情况,尽量在cpp中实现代码,保持头文件的简洁和轻巧。

发表于 2014年04月14日 23:42   评论:0   阅读:2700  



回到顶部

首页 | 关于我 | 关于本站 | 站内留言 | rss
python logo   django logo   tornado logo