美文网首页
windbg分析内存泄漏

windbg分析内存泄漏

作者: 睡在床板下 | 来源:发表于2019-01-23 09:26 被阅读0次

    介绍

    本文主要介绍一种通过windbg分析内存泄漏的方法。

    现象

    后台检测程序在某天上报了告警,大概就是某程序的提交内存达到了1.0G。登陆后台查看,该进程已经运行了90天,提交内存每天都在持续上涨,从启动到目前为止大概累计上升了800M。应该是存在内存泄漏。
    让运维通过工具保存了fulldump

    准备工作

    e:\mylocalsymbols;SRV*e:\mylocalsymbols*http://msdl.microsoft.com/download/symbols
    
    

    查找堆块

    打印所有堆块信息

    !heap -s
    
    

    显示如下

    0:000> !heap -s
    HEAPEXT: Unable to read ntdll!RtlpDisableHeapLookaside
      Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                        (k)     (k)    (k)     (k) length      blocks cont. heap 
    -----------------------------------------------------------------------------
    006f0000 00000002 1246976 1241928 1246976    982   236    81    0      a   LFH
    00190000 00001002    3136   1564   3136    390     7     3    0      0   LFH
        External fragmentation  24 % (7 free blocks)
    00110000 00001002     256      4    256      1     1     1    0      0      
    02050000 00001002     256    176    256      1    18     1    0      0   LFH
    02240000 00001002     256      4    256      2     1     1    0      0      
    006a0000 00001002      64     12     64      4     2     1    0      0      
    044f0000 00001002     256    216    256      7     4     1    0      0   LFH
    119d0000 00001002    7424   5820   7424    134   133     4    0     c8   LFH
    14290000 00001003     256      4    256      2     1     1    0    bad      
    141d0000 00001003     256      4    256      2     1     1    0    bad      
    17f20000 00001003     256      4    256      2     1     1    0    bad      
    19030000 00001003     256      4    256      2     1     1    0    bad      
    191b0000 00001003     256      4    256      2     1     1    0    bad      
    19380000 00001003     256      4    256      2     1     1    0    bad      
    19300000 00001003     256      4    256      2     1     1    0    bad      
    155f0000 00001003     256      4    256      2     1     1    0    bad      
    -----------------------------------------------------------------------------
    
    

    通过观察,我们知道了是006f0000堆块占用了大量内存

    HEAPEXT: Unable to read ntdll!RtlpDisableHeapLookaside
      Heap     Flags   Reserv  Commit  Virt   Free  List   UCR  Virt  Lock  Fast 
                        (k)     (k)    (k)     (k) length      blocks cont. heap 
    -----------------------------------------------------------------------------
    006f0000 00000002 1246976 1241928 1246976    982   236    81    0      a   LFH
    
    

    查看堆块内存百分比

    内存持续上涨可能是某块固定大小内存被重复申请,所以统计下该堆块中各个内存大小的分配次数

    !heap -stat -h 006f0000  
    
    

    查找堆中各个内存大小占用的百分比

    0:000> !heap -stat -h 006f0000
    unable to resolve ntdll!RtlpStackTraceDataBase
     heap @ 006f0000
    group-by: TOTSIZE max-display: 20
        size     #blocks     total     ( %) (percent of total busy bytes)
        14 23acbbe - 2c97ead8  (92.78)
        a4 2ba0c - 1bf2fb0  (3.63)
        1000 8f5 - 8f5000  (1.16)
        1a4 3b9c - 61cbf0  (0.79)
        20c 15fb - 2cfdc4  (0.37)
        25 b77d - 1a8511  (0.22)
        64 3ba0 - 174a80  (0.19)
        24 75ae - 108c78  (0.13)
        11c e4a - fda18  (0.13)
        84c 164 - b89b0  (0.09)
        400 172 - 5c800  (0.05)
        234 265 - 54684  (0.04)
        1c 2c2e - 4d508  (0.04)
        1c0 287 - 46c40  (0.04)
        c00 4b - 38400  (0.03)
        20 1a12 - 34240  (0.03)
        3bc ce - 30148  (0.02)
        50 8da - 2c420  (0.02)
        800 4c - 26000  (0.02)
        2ba d2 - 23c94  (0.02)
    
    
        size     #blocks     total     ( %) (percent of total busy bytes)
        14 23acbbe - 2c97ead8  (92.78)
    
    

    TOP 20 中显示,最多的一个大小为 0x014 的分配次数为 0x23acbbe 次, 总共大概有700M左右。基本接近内存泄漏的总数。那么我们就需要来确定这个内存是谁申请的。

    定位内存来源

    找到了大量的内存是0x014字节大小的,但是根据这个条件我们也找不到具体的代码啊?下面是几个思路

    思路1 根据大小

    根据内存大小(0x14)去代码中查找大小为(0x14)的类、结构体、宏等等相关代码,然后找到原因。
    难!!!
    1)、进程包含了很多其他组的dll,有的我没代码权限,无法遍历
    2)、结构体、类太多了,人眼遍历太难了(针对这个问题我开发了一个工具,后续章节讲解)

    思路2 内存内容

    显示所有大小为(0x14)内存的地址,看它的地址内容有没有什么特点,比如是否有特殊的字符串、固定的二进制头??? 显示所有分配大小为 0x14的内存

    !heap -flt s 14 
    
    
    0:000> !heap -flt s 14 
    unable to resolve ntdll!RtlpStackTraceDataBase
        _HEAP @ 6f0000
          HEAP_ENTRY Size Prev Flags    UserPtr UserSize - state
            0071c038 0004 0000  [00]   0071c040    00014 - (busy)
            0071c2e8 0004 0004  [00]   0071c2f0    00014 - (busy)
            0071e498 0004 0004  [00]   0071e4a0    00014 - (busy)
            0071e4f8 0004 0004  [00]   0071e500    00014 - (busy)
            0071e518 0004 0004  [00]   0071e520    00014 - (busy)
            0071e5f8 0004 0004  [00]   0071e600    00014 - (busy)
            0071e638 0004 0004  [00]   0071e640    00014 - (busy)
            0071e658 0004 0004  [00]   0071e660    00014 - (busy)
            0071e798 0004 0004  [00]   0071e7a0    00014 - (busy)
            007374f0 0004 0004  [00]   007374f8    00014 - (busy)
            00737510 0004 0004  [00]   00737518    00014 - (busy)
            00737530 0004 0004  [00]   00737538    00014 - (busy)
            00737550 0004 0004  [00]   00737558    00014 - (busy)
            00737570 0004 0004  [00]   00737578    00014 - (busy)
            00737590 0004 0004  [00]   00737598    00014 - (busy)
            007375b0 0004 0004  [00]   007375b8    00014 - (busy)
            007375d0 0004 0004  [00]   007375d8    00014 - (busy)
            007375f0 0004 0004  [00]   007375f8    00014 - (busy)
            00737610 0004 0004  [00]   00737618    00014 - (busy)
            00737630 0004 0004  [00]   00737638    00014 - (busy)
            00737650 0004 0004  [00]   00737658    00014 - (busy)
            00737670 0004 0004  [00]   00737678    00014 - (busy)
            00737690 0004 0004  [00]   00737698    00014 - (busy)
                ..............
                ..............
    
    

    随机抽查几个地址,看下地址内存


    image

    大都是这样的值,实在是看不出规律。

    建议

    一般公司都会封装malloc、new函数,并分配一个模块号,每个内存地址头部都会携带id号,如下:

    xxx_malloc(int nModleID,size_t size);
    
    

    这样通过地址空间内容也可以找到分配的模块。

    思路3 分配次数

    大小0x14的内存在90天时间内总共分配了23acbbe 次, 0x23acbbe = 37407678/(90(天)*24(小时) ≈ 17318次/小时。 这个内存几乎每小时被申请17318次。公司的服务器有个基本功能:每个小时会统计收到的消息次数,那分析下数量级在1w~3w左右的消息即可,大概是4个消息类型,然后通过代码review发现内存泄漏点

    if(total_fee){
        LPADD_FEE pAddFee = new ADD_FEE;
        ZeroMemory(pAddFee, sizeof(ADD_FEE));
        pAddFee->nFee = total_fee;
        gdt.nTotalFee = total_fee;
    }
    
    

    结构体 ADD_FEE ,刚好是20字节

    typedef struct _tagADD_FEE{
        int nFee;
        int nReserved[4];
    }ADD_FEE, *LPADD_FEE;
    
    

    完全符合!! 问题解决

    总结

    这个一个低级错误导致的。为了避免类视问题,引入代码静态检测
    1)、cppcheck
    2)、pclint
    最后选了pclint。配合jenkins,每天凌晨进行代码静态检查,并输出和上个版本的diff文件,下次就不会出现这么低级的问题。

    相关文章

      网友评论

          本文标题:windbg分析内存泄漏

          本文链接:https://www.haomeiwen.com/subject/fszdjqtx.html