V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
matthewgao
V2EX  ›  C

如何处理大型 C++程序的内存泄露问题

  •  
  •   matthewgao · 2015-07-29 19:58:51 +08:00 · 2186 次点击
    这是一个创建于 3384 天前的主题,其中的信息可能已经有所发展或是发生改变。

    程序在一个月的运行中不断吃内存,一个月吃掉了32G的内存,但是线上系统还不能停,不能改配置。。。。 有什么好办法排除内存泄露的问题么?

    第 1 条附言  ·  2015-07-29 22:03:27 +08:00
    程序时个daemon,监听端口处理请求,不是一个可复现的问题,因为同样的系统从来没出过问题,所以怀疑时配置了某种不长用的配置导致问题出现。
    重新看一遍代码是最后的办法,但是很困难,代码量太大
    31 条回复    2016-02-20 23:49:58 +08:00
    linxy
        1
    linxy  
       2015-07-29 20:01:45 +08:00
    换go
    matthewgao
        2
    matthewgao  
    OP
       2015-07-29 20:03:35 +08:00
    @linxy 不现实。。。
    acros
        3
    acros  
       2015-07-29 20:11:43 +08:00
    占个座位等解答。
    这种环境下,是不是只能靠内存dump分析了?
    fo2w
        4
    fo2w  
       2015-07-29 20:15:32 +08:00
    看描述我完全不知道你是要解决问题还是排查问题
    不停机不能改配置要解决问题那无解, 或者我太废柴暂时没想到办法
    要查问题这种情况下钩子就好了

    但是...我还是建议好好review一遍代码...
    这种泄漏量肉眼我真不信看不出来
    sinxccc
        5
    sinxccc  
       2015-07-29 20:16:13 +08:00
    我们常用的做法是首先复制一个尽可能跟在线系统一样的环境,然后如果性能说得过去的话用 valgrind 跑可疑进程,对性能要求高的话,加上自己包装过的 new/delete/malloc/free 然后跑一段时间看输出的统计。

    分离出最可疑的地方之后再按照这个特征走查代码。
    lijianying10
        6
    lijianying10  
       2015-07-29 20:20:45 +08:00   ❤️ 1
    不知道是何种业务,
    1. 取一份程序拷贝准备复现问题
    2. 两个方面来确定问题的来源
    1. 用更高的业务访问频率(业务参数)来模拟在线程序的情况,让问题显现更清楚
    2. 满足上面一条之后,(如果是巨大循环的算法)每次注释掉一个模块,来排查问题。找到是那个大模块之后,再从大模块入手使用同样方法,直到找到泄露点。
    1. 尽量避免注释掉依赖模块。从底层开始。
    2. 模块的划分粒度大小很重要,影响排查速度
    3. 如果是由独立业务堆砌出来的大型程序,要从业务划分来找到一个或者多个业务发生泄露,然后使用上面的方法
    4. 如果十分确定使用上面两条找不到。最后一招CodeReview。
    5. 找到来源之后修复一下,做项目回归测试。

    希望此流程能帮到楼主。这也是我在做计算程序的时候排查泄露时候的方法。
    ch3n2k
        7
    ch3n2k  
       2015-07-29 20:32:34 +08:00
    加大swap,让它泄漏去。话说什么程序不能停机啊?要改架构啊
    typcn
        8
    typcn  
       2015-07-29 20:42:31 +08:00
    @ch3n2k 内存泄露只是丢失访问途径,而且还非常容易造成程序的崩溃,仅仅占内存的话,那叫内存申请不合理,没有释放

    还是用 valgrind 跑吧,手找累死人
    zhouc
        9
    zhouc  
       2015-07-29 22:03:47 +08:00
    你们的程序是server程序,不能load balance traffic?
    jedihy
        10
    jedihy  
       2015-07-29 22:16:44 +08:00 via iPhone
    对,valgrind,差不多就这些办法。一般单元测试的时候就要按模块找出内存泄露,不然耦合到一起很难找了。
    lsmgeb89
        11
    lsmgeb89  
       2015-07-29 22:25:29 +08:00
    大型的 C/C++ 程序,一开始开始就要考虑到内存管理模块啊,团队要制定好内存使用的规则,这样出现内存泄漏就可以定位到那个 instance 和代码位置。

    PS:用 Valgrind 跑很慢的,而且不一定能够定位的。
    xylophone21
        12
    xylophone21  
       2015-07-29 22:38:08 +08:00   ❤️ 1
    #define new(x) mynew(__FILE__,__LINE__,x);
    signifox
        13
    signifox  
       2015-07-29 23:32:04 +08:00
    gcc4.8以上版本用[AddressSanitizer]{https://code.google.com/p/address-sanitizer/wiki/AddressSanitizer},
    低版本的用[efence]{https://directory.fsf.org/wiki/Electric_Fence}

    检查C++内存泄露的神器。
    alphonsez
        14
    alphonsez  
       2015-07-30 00:07:20 +08:00
    建议改成能重启的。老不能重启总是个麻烦,总不见得永远不部署吧?如果能重启就好办了,每天重启一次呗。
    akira
        15
    akira  
       2015-07-30 00:59:37 +08:00
    如果有完善的日志的话,可以试试从日志里分析出业务量变化和内存增加变化的关系。定位到业务的话 就比较好做代码review了
    dndx
        16
    dndx  
       2015-07-30 01:07:21 +08:00 via iPhone
    用智能指针。
    JamesRuan
        17
    JamesRuan  
       2015-07-30 02:01:11 +08:00
    上Erlang/OTP
    vietor
        18
    vietor  
       2015-07-30 08:16:50 +08:00 via Android
    所有内存分配回收代码都过一遍, 尤其用指针封装的地方(智能指针,共享指针),许多人没理解就乱用,用错。我之前就将所有智能指针转成自写封装类,解决内存泄漏
    nightv2
        19
    nightv2  
       2015-07-30 09:43:08 +08:00
    重启啊,至少能改善问题,然后排查是那个模块
    qyz0123321
        20
    qyz0123321  
       2015-07-30 09:50:33 +08:00
    智能指针是王道
    eliteYang
        21
    eliteYang  
       2015-07-30 10:13:16 +08:00
    强烈推荐intel vtunes分析,或者全局用智能指针,或者全局排查指针申请和释放的地方,看看有没有因为什么情况导致申请完后一些条件不满足,没有走到释放
    hitmanx
        22
    hitmanx  
       2015-07-30 10:30:42 +08:00
    生产环境不能停机的话,可以开发环境重新跑一次,并打开valgrind\address sanitizer之类的工具在运行时检测一下内存泄露。如果这也不行的话,可以上一些静态代码分析工具看看,比如coverity之类的
    acgeo
        23
    acgeo  
       2015-07-30 11:25:18 +08:00
    楼上各位说得都是扯犊子的~~

    这种情况,吃内存是无法避免的~

    写个shell脚本 定时执行~ 这样每次执行完毕,进程结束会自动释放掉内存.
    pp3182429
        24
    pp3182429  
       2015-07-30 13:29:40 +08:00
    以前每次都是check所有分配内存的地方,是不是释放了…找所有的new和malloc==#。。
    acgeo
        25
    acgeo  
       2015-07-30 15:54:28 +08:00
    楼上各位说得都是扯犊子的~~

    这种情况,吃内存是无法避免的~

    写个shell脚本 定时执行~ 这样每次执行完毕,进程结束会自动释放掉内存.
    linux40
        26
    linux40  
       2015-07-30 16:27:50 +08:00
    为什么不用标准库里的模板?虽然我不是很懂工程这种东西。。。
    magicyu1986
        27
    magicyu1986  
       2015-07-30 17:07:28 +08:00
    上测试环境一块一块的排查,真没有其他好办法。

    最好还是上一套半自动内存管理的东西:智能指针,循环引用检查啥的。
    acgeo
        28
    acgeo  
       2015-07-30 19:37:33 +08:00
    楼上各位说得都是扯犊子的~~

    这种情况,吃内存是无法避免的~

    写个shell脚本 定时执行~ 这样每次执行完毕,进程结束会自动释放掉内存.!
    matthewgao
        29
    matthewgao  
    OP
       2015-07-30 22:32:40 +08:00
    程序比较老,写的时候没有一个好用的智能指针,然后是一个大牛自己写的智能指针,诡异的就在于各个版本从来没出过问题,就这一台机器,同版本的其他机器也没出过问题,这个机器也没什么特别特殊的配置,所以很诡异
    我现在怀疑是STL,有些容器不会自动shrink的问题。
    matthewgao
        30
    matthewgao  
    OP
       2015-07-30 22:33:10 +08:00
    总之还是谢谢各位,我去试试AddressSanitizer,看看有什么效果不
    matthewgao
        31
    matthewgao  
    OP
       2016-02-20 23:49:58 +08:00
    我来做个了结,最终还是靠 review 代码, 找到了泄露的地方, 10w 行。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1481 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 29ms · UTC 17:17 · PVG 01:17 · LAX 10:17 · JFK 13:17
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.