• C++ 内存泄漏检测与实现


    内存泄漏

    malloc/new 调用在堆上分配的内存却没有相应的free/delete;

    带来的问题

    • 会逐渐吃掉虚拟内存

    难点

    如何判断是否内存泄漏

    1. (最毛糙)可以使用+1和-1,即当调用分配内存变量+1,释放内存时候,变量-1,进程退出输出的count=0,进程内存没有泄漏,否则有泄漏;
    2. 线上版本,做一个配置文件,设置是否存在内存泄漏的一个标志位,平时肉眼观察不出的内存泄漏,在长时间的运行积攒下,肯定会逐渐变大,然后当可以观察出有内存泄漏了,就将标志位置位1;进行热更新,释放相应的泄漏处理(如 hook);

    如何判断在代码中哪里泄漏

    • 当已经怀疑有泄漏:使用简单的mtrace直接检查代码也是一个简单的操作。
    • hook实现泄漏判断与追踪:实现本地检查或者在线的全局实时检查(线上版本,写一个conf配置文件,热更新是否使用hook)
    • 宏定义实现 hook: 实现单个文件检查
    • 调用malloc的时候,自己构建一个内存池(慢慢合成),每次释放的时候,从池中删除。最终在池中的就是泄露的;

    hook实现泄漏判断与追踪 (malloc和free重载)

    #define _GNU_SOURCE
    #include 
    #include 
    #include 
    #include 
    
    #define MEM_CONTENT_LEN 128
    
    typedef void *(*malloc_t)(size_t size);
    malloc_t malloc_f = NULL;
    
    typedef void(*free_t)(void *P);
    free_t free_f;
    
    int enable_malloc_hook = 1;
    int enable_free_hook = 1;
    
    void *malloc(size_t size){
        if (enable_malloc_hook)
        {
            enable_malloc_hook = 0;         //防止递归;不这么设计会发生循环调用  ,printf也会调用malloc  直接打印观察是否泄漏
            void *p = malloc_f(size);   
            // printf("malloc\n");              
    
            /*
            参数:
               0:当前函数在哪调用:  返回==f()-->malloc
               1: a()-->f()-->malloc
               2: a()-->b()-->f()-->malloc        
            */
            void *caller = __builtin_return_address(0); 
            char buff[MEM_CONTENT_LEN] = {0}; //具体是否泄漏,何处泄漏,打印到文件中
            sprintf(buff,"./mem/%p.mem",p);
            FILE *fp = fopen(buff,"w");
            fprintf(fp,"[+%p]malloc --> addr: %p,size: %lu\n",caller,p,size);
            fflush(fp);
            
            enable_malloc_hook = 1;
            return p;
        }
        else{
            return malloc_f(size);
        }    
    }
    
    void free(void *p){
        // printf("free\n");
        if (enable_free_hook)
        {
            enable_free_hook = 0;
            char buff[MEM_CONTENT_LEN] = {0}; //具体是否泄漏,何处泄漏
            sprintf(buff,"./mem/%p.mem",p);
            if (unlink(buff) <0)
            {
                printf("double free:%p\n",p);
            }
            enable_free_hook= 1;
        }else{
            free_f(p);
        }    
    }
    
    static int init_hook(){
        malloc_f = dlsym(RTLD_NEXT,"malloc");
        free_f = dlsym(RTLD_NEXT,"free");
    }
    
    int main(){
        init_hook(); // hook调用
        void *p = malloc(10);
        free(p);
        void *p1 = malloc(20);
        void *p2 = malloc(30);
        free(p1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    gcc -o hook hook.c -ldl  #编译
    mkdir  mem  #存储文件
    ./hook   #进行检查,若mem中生成了文件,则说明出了内存泄漏的问题
    cat mem/0x55a3d3e666c0.mem  # 具体泄露:[+0x55a3d381a59e]malloc --> addr: 0x55a3d3e666c0,size: 30
    
    addr2line -fe ./hook -a 0x55a3d381a59e#检查具体泄露的出现代码地址
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-r5AtHEAC-1659580555150)(C:\Users\11606\AppData\Roaming\Typora\typora-user-images\image-20220726183617643.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pZNI3BPo-1659580555151)(C:\Users\11606\AppData\Roaming\Typora\typora-user-images\image-20220726215055106.png)]

    宏定义实现 hook

    #define _GNU_SOURCE
    #include 
    #include 
    #include 
    #include 
    
    #define MEM_CONTENT_LEN 128
    
    void *malloc_hook(size_t size,const char* file, int line){
        void *p = malloc(size);
        char buff[MEM_CONTENT_LEN] = {0}; //具体是否泄漏,何处泄漏,打印到文件中
        sprintf(buff,"./mem/%p.mem",p);
        FILE *fp = fopen(buff,"w");
        fprintf(fp,"[+%s:%d]malloc --> addr: %p,size: %lu\n",file,line,p,size);
        fflush(fp);
        return p;
    }
    
    void* free_hook(void *p,const char* file,int line)
    {
        char buff[MEM_CONTENT_LEN] = {0}; //具体是否泄漏,何处泄漏
        sprintf(buff,"./mem/%p.mem",p);
        if (unlink(buff) <0)
        {
            printf("double free:%p\n",p);
        }
        free(p);
    }
    //如果宏定义放在函数实现前面,会出现循环调用
    #define malloc(size)        malloc_hook(size,__FILE__,__LINE__)  
    #define free(p)     free_hook(p,__FILE__,__LINE__)
    
    int main(){
        void *p = malloc(10);
        free(p);
        void *p1 = malloc(20);
        void *p2 = malloc(30);
        free(p1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CiAcpUGx-1659580555151)(C:\Users\11606\AppData\Roaming\Typora\typora-user-images\image-20220726220204261.png)]

    其中[+hook_def.c:39]说明了文件中的多少行出了问题;

  • 相关阅读:
    DIVFusion_ Darkness-free infrared and visible image fusion 论文解读
    【CGAL_网格处理】Isotropic Remeshing均匀化网格
    基本计算器问题
    7.pandas缺失值处理(表格数据处理)
    贝叶斯公式——假阳性问题
    【Python】第三课 分支和循环的使用
    Arduino 电机测速
    BIOMOD2模型、MaxEnt模型物种分布模拟,生物多样性生境模拟,论文写作
    解决 MyBatis-Plus + PostgreSQL 中的 org.postgresql.util.PSQLException 异常
    sql高级语法的应用
  • 原文地址:https://blog.csdn.net/qq_41214679/article/details/126154911