立即注册
查看: 664|回复: 0

[linux技术资料] C++程序员应对内存泄露的5个解决方法

已绑定手机
发表于 2021-3-31 10:09:44 | 显示全部楼层 |阅读模式 来自 广东省深圳市
在 C++ 程序中,主要涉及到的内存就是“栈”和“堆”,通常来说,一个线程的栈内存是有限的,通常来说是 8M 左右(取决于运行的环境)。栈上的内存通常是由编译器来自动管理的。当在栈上分配一个新的变量时,或进入一个函数时,栈的指针会下移,相当于在栈上分配了一块内存。我们把一个变量分配在栈上,也就是利用了栈上的内存空间。当这个变量的生命周期结束时,栈的指针会上移,相同于回收了内存。由于栈上的内存的分配和回收都是由编译器控制的,所以在栈上是不会发生内存泄露的,只会发生栈溢出(Stack Overflow),也就是分配的空间超过了规定的栈大小。而堆上的内存是由程序直接控制的,程序可以通过 malloc/free 或 new/delete 来分配和回收内存,如果程序中通过 malloc/new 分配了一块内存,但忘记使用 free/delete 来回收内存,就发生了内存泄露。
图片1.png

方法1:尽量避免在堆上分配内存
既然只有堆上会发生内存泄露,那第一原则肯定是避免在堆上面进行内存分配,尽可能的使用栈上的内存,由编译器进行分配和回收,这样当然就不会有内存泄露了。
然而,只在栈上分配内存,在有 IO 的情况下是存在一定局限性的。
举个例子,为了完成一个请求,我们通常会为这个请求构造一个 Context 对象,用于描述和这个请求有关的一些上下文。例如下面一段代码:
void Foo(Reuqest* req) {
    RequestContext ctx(req);
    HandleRequest(&ctx);
}
如果 HandleRequest 是一个同步函数,当这个函数返回时,请求就可以被处理完成,那么显然 ctx 是可以被分配在栈上的。
但如果 HandleRequest 是一个异步函数,例如:
void HandleRequest(RequestContext* ctx, Callback cb);
那么显然,ctx 是不能被分配在栈上的,因为如果 ctx 被分配在栈上,那么当 Foo 函数推出后,ctx 对象的生命周期也就结束了。而 FooCB 中显然会使用到 ctx 对象。
void HandleRequest(RequestContext* ctx, Callback cb);
void Foo(Reuqest* req) {
    auto ctx = new RequestContext(req);
    HandleRequest(ctx, FooCB);
}
void FooCB(RequestContext* ctx) {
    FinishRequest(ctx);
    delete ctx;
}
在这种情况下,如果忘记在 FooCB 中调用 delete ctx,则就会触发内存泄露。尽管我们可以借助一些静态检查工具对代码进行检查,但往往异步程序的逻辑是极其复杂的,一个请求的生命周期中,也需要进行大量的内存分配操作,静态检查工具往往无法发现所有的内存泄露情况。那么怎么才能避免这种情况的产生呢?引入智能指针显然是一种可行的方法,但引入shared_ptr往往引入了额外的性能开销,并不十分理想。在SmartX,我们通常采用两种方法来应对这种情况。



更多方法请下载附件查看
游客,如果您要查看本帖隐藏内容请回复


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

合作/建议

TEL: 19168984579

工作时间:
周一到周五 9:00-11:30 13:30-19:30
  • 扫一扫关注公众号
  • 扫一扫打开小程序
Copyright © 2013-2024 一牛网 版权所有 All Rights Reserved. 帮助中心|隐私声明|联系我们|手机版|粤ICP备13053961号|营业执照|EDI证
在本版发帖搜索
扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表