c/c++ 内存泄漏

内存泄漏(memory leakage)概述

定义:在编写应用程序的时候,程序分配了一块内存,但已经不再持有引用这块内存的对象(通常是指针),虽然这些内存被分配出去,但是无法收回,将无法被其他的进程所使用,我们说这块内存泄漏了,被泄漏的内存将在整个程序声明周期内都不可使用。
主要原因:是在使用new或malloc动态分配堆上的内存空间,而并未使用delete或free及时释放掉内存。

内存泄漏的场景

1. malloc和free未成对出现

{
    char *p=(char *)malloc(sizeof(char) *10);
    memset(p,0,sizeof(char)*10);
   ...
    return 0;
}

上面的程序在编译,运行阶段都没有问题,但是由malloc分配的内存去无法回收。可以利用valrind定位内存泄漏的原因,如图用malloc分配了10 bytes内存,但是并未回收,valgrind --tool=memcheck --leak-check=full ./test

valgrind01.png

2. 通过局部分配的内存,未在调用者函数体内释放

这种错误在大型代码中会常常放,而且出现问题不容易定位,因为内存分配与释放是在不同的函数体内。

char* getMemory()
{
    char *p = (char *)malloc(30); 
    return p;
}
 
int main()
{
    char *p = getMemory();
    
    return 0;
}
valgrind02.png

在函数调用者的内部正确释放内存,代码如下。

char* getMemory()
{
    char *p = (char *)malloc(30); 
    return p;
}
 
int main()
{
    char *p = getMemory();

    if(p != NULL)
    {
        free(p);
        p=NULL;
    }
    return 0;
}

3. 在局部分配的内存未释放

void getheap(int *p)//p是NULL的地址
{
    p = malloc(sizeof(int) * 10); //p重新指向了分配在堆中的空间
    memset(p,0,sizeof(int) * 10);
}//形式参数int *p在栈空间内,函数结束后就释放了,malloc分配的空间也丢失了.  
int main()
{
    int *p = NULL;  //NULL就是(void *)0
    printf("p=%p\n", p);//p是null的地址
    printf("p=%p\n", &p);//&p是p本身的地址
 
    getheap(p);//值传递,将NULL的地址传递给形参
 
    p[0] = 10;//相当与给NULL指针赋值
    p[1] = 20;
    printf("p[0]=%d,p[1]=%d\n",p[0],p[1]);  
    
    free(p);//p中不是堆中分配的空间的首地址,故free(p)也有问题
    //int *ptr = (int *)malloc(10);
    return 0;
}

程序在编译阶段并未报错,当时会出现段错误(内存未能正确被访问,如访问位初始化的指针,访问已经释放的指针变量,数组越界等),可以使用gdb调试coredump定位错误原因。

seg01.png

上述代码中,在函数getheap中分配的内存,但并未能得到释放,当该函数返回时,因为未返回分配的内存的首地址,所以程序将失去对这块内存的控制。

valgrind04_1.png

4. 由于程序运行时出现不可遇见的错误,导致内存泄漏。

int fun1()
{
    p = malloc(sizeof(int) * 10); 
    fun2();
    ...
    free(p);
}

上述代码段在运行中时,若fun2内部出现错误,但是free函数不能正常执行,所以出现的内存泄漏的情况。

其他出现段错误的情况,使用未初始化的内存;在内存被释放后进行读/写;从已分配内存块的尾部进行读/写;不匹配地使用malloc/new/new[] 和 free/delete/delete[];两次释放内存等。并利用valgrind检测

c++中内存泄漏情况

1. 不匹配使用new[] 和 delete[]

int *p = new int[100];
delete []p;//new[],delete []不匹配,导致99对象的内存空间被泄漏。

2. delet void * 的指针,导致没有调用到对象的析构函数,析构的所有清理工作都没有去执行从而导致内存的泄露;

class Object {
private:
    void* data;
    const int size;
    const char id;
public:
    Object(int sz, char c):size(sz), id(c){
        data = new char[size];
        cout << "Object() " << id << " size = " << size << endl;
    }
    ~Object(){
        cout << "~Object() " << id << endl;
        delete []data;      
    }
};
int main() {
    Object* a = new Object(10, 'A');//Object*指针指向一个Object对象;
    void* b = new Object(20, 'B');//void*指针指向一个Object对象;
    delete a;//执行delete,编译器自动调用析构函数;
    delete b;//执行delete,编译器不会调用析构函数,导致data占用内存没有得到回收;

    return 0;
}

执行结果:可见对象b的析构函数并未调用,在用delete删除b的操作中,我们只是对b指向的对象进行了释放,并没有调用析构函数,也就是说data指向的内存并没有被释放,而此时又没有指针指向刚才data指针指向的内存,从而造成了内存的丢失,更会造成内存的泄露。

valgrind11.png

valgrind分析的结果:可见出现了20 bytes的内存泄漏

valgrind12.png

3. 没有将基类的析构函数定义为虚函数,当基类的指针指向子类时,delete该对象时,不会调用子类的析构函数

class person{
public:
    person(){
        cout<<"基类构造函数运行中.....\n";
    }
    ~person(){
        cout<<"基类析构函数运行中.....\n";
    }//需要设置为virtual函数
}; 
class child:public person{
private:
    void* data;
    const int size;
    const char id;
public:
    child(int sz, char c):size(sz), id(c){
        data = new char[size];
        cout<<"派生类构造函数运行中.....\n";
        cout << "child() " << id << " size = " << size << endl;
    }
    ~child(){
        delete []data;
        cout<<"派生类析构函数运行中.....\n";
    }
};
int main(int argc, char** argv) {
    
    person *p = new child(20,'B');
    delete p;
    return 0;
}

发现派生类的析构函数并未运用,这时析构函数不能够将构造函数建立的动态资源,进行有效的释放。有效的做法是将父类的析构函数的设为虚函数(vartual)

valgrind13.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容