处理c++内存泄露的方法

c++内存泄露,常由于程序中未释放不再使用的动态分配的内存导致。因此,真正理解所需要的分配的内存对象的范围大小是很有必要的。更重要的是,要明白何时调用 free。但当程序复杂度增加时,要确定 free 的调用时机将变得更加困难。早期设计决策时,规划内存很重要。下面西安达内c++培训讲师就详细为大家介绍。

 

以下是处理c++内存泄露的技能表:

 

1) 启动时分配

想让内存管理保持简单,一个方法是在启动时在堆中分配所有所需的内存。程序结束时,释放内存的重任就交给了操作系统。这种方法在许多场景中的效果令人满意,特别是当程序在一个批量操作中完成对输入的处理的情况。

 

2) 变长数组

如果你需要有着变长大小的临时存储,并且其生命周期在变量内部时,可考虑VLAVariable Length Array,变长数组)。但这有个限制:每个函数的空间不能超过数百字节。因为C99指出边长数组能自动存储,它们像其他自动变量一样受限于同一作用域。即便标准未明确规定,VLA的实现都是把内存数据放到栈中。VLA的最大长度为SIZE_MAX字节。考虑到目标平台的栈大小,我们必须更加谨慎小心,以保证程序不会面临栈溢出、下个内存段的数据损坏的尴尬局面。

 

3) 自己编写引用计数

这个技术的想法是对某个内存对象的每次引用、去引用计数。赋值时,计数器会增加;去引用时,计数器减少。当引用计数变为0时,这意味着此内存对象不再被使用,可以释放。因为C不提供自动析构(事实上,GCCClang都支持cleanup语言扩展), 也不是重写赋值运算符,引用计数由调用retain/release的函数手动完成。更好的方式,是把它作为程序的可变部分,能通过这部分获取和释放一个内存对象的拥有权。但是,使用这种方法需要很多(编程)规范来防止忘记调用release(停止内存泄露)或不必要地调用释放函数(这将导致内存释放地过早)。若内存对象的生命期需要外部事件指出,或应用程序的数据结构隐含了某个内存对象的持有权的处理,无论何种情况,都容易导致问题。下述代码块含有简化了的内存管理引用计数。

 

#include

#include

 

#define MAX_REF_OBJ 100

#define RC_ERROR -1

 

struct mem_obj_t{

    void *ptr;

    uint16_t count;

};

 

static struct mem_obj_t references[MAX_REF_OBJ];

static uint16_t reference_count = 0;

 

/* create memory object and return handle */

uint16_t create(size_t size){

 

    if (reference_count >= MAX_REF_OBJ)

        return RC_ERROR;

 

    if (size){

        void *ptr = calloc(1, size);

 

        if (ptr != NULL){

            references[reference_count].ptr = ptr;

            references[reference_count].count = 0;

            return reference_count++;

        }

    }

 

    return RC_ERROR;

}

 

/* get memory object and increment reference counter */

void* retain(uint16_t handle){

 

    if(handle < reference_count && handle >= 0){

        references[handle].count++;

        return references[handle].ptr;

    } else {

        return NULL;

    }

}

 

/* decrement reference counter */

void release(uint16_t handle){

    printf("release\n");

 

    if(handle < reference_count && handle >= 0){

        struct mem_obj_t *object = &references[handle];

 

        if (object->count <= 1){

            printf("released\n");

            free(object->ptr);

            reference_count--;

        } else {

            printf("decremented\n");

            object->count--;

        }

    }

}

如果你关心编译器的兼容性,可用 cleanup 属性在C中模拟自动析构。

 

void cleanup_release(void** pmem) {

    int i;

    for(i = 0; i < reference_count; i++) {

        if(references[i].ptr == *pmem)

           release(i);

    }

}

 

void usage() {

    int16_t ref = create(64);

 

    void *mem = retain(ref);

    __attribute__((cleanup(cleanup_release), mem));

 

    /* ... */

}

上述方案的另一缺陷是提供对象地址让 cleanup_release 释放,而非引用计数值。这样一来,cleanup_release 必须在 references 数组中做开销大的查找操作。一种解决办法是,改变填充的接口为返回一个指向 struct mem_obj_t 的指针。另一种办法是使用下面的宏集合,这些宏能够创建保存引用计数值的变量并追加 clean 属性。

 

/* helper macros */

#define __COMB(X,Y) X##Y

#define COMB(X,Y) __COMB(X,Y)

#define __CLEANUP_RELEASE __attribute__((cleanup(cleanup_release)))

 

#define retain_auto(REF) retain(REF); int16_t __CLEANUP_RELEASE COMB(__ref,__LINE__) = REF

 

void cleanup_release(int16_t* phd) {

    release(*phd);

}

 

void usage() {

    int16_t ref = create(64);

 

    void *mem = retain_auto(ref);

    /* ... */

}

(注:##符号源自C99,用于连接两个变量的名称,一般用在宏里。如int a##b就会定义一个叫做ab的变量;__LINE__指代码行号,类似的还有__FUNCTION____func____FILE__,可用于打印调试信息;__attribute__符号来自gcc,主要用于指导编译器优化,也提供了一些如构造、析构、字节对齐等功能)

来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/29562929/viewspace-1220498/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/29562929/viewspace-1220498/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值