• windows下C/C++的内存泄露检测


    windows下C/C++的内存泄露检测

    https://www.andseclab.com/2018/04/17/windows%E4%B8%8Bc-c%E7%9A%84%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2%E6%A3%80%E6%B5%8B/

    C/C++由于其没有垃圾回收机制,所以内存的释放一直以来都依靠于程序员的手工释放,因此极其容易出现内存泄露的问题,而在比较大的程序之中,查找内存泄露是一件比较困难的事情,所以我们需要一些简便的方法来检测内存泄露,避免内存泄露导致设备崩溃。

    检测方法

    利用Visual Studio调试器和CRT库提供的检测(malloc和new均适用)

    检测内存泄露

    程序只从单一位置退出时:

    1.需要以下头文件:

    #define _CRTDBG_MAP_ALLOC
    #include<stdlib.h>
    #include<crtdbg.h>
    

    通过包含crtdbg.h ,将malloc和free分别映射到_malloc_dbg和_free_dbg,用于内存分配和释放的跟踪。
    #define _CRTDBG_MAP_ALLOC语句用于提供额外的信息,非绝对必要。

    2.在程序退出位置前使用以下语句:

    _CrtDumpMemoryLeaks();
    

    该语句会在输出窗口显示内存泄露信息。

    测试代码
    #define _CRTDBG_MAP_ALLOC
    #include<stdio.h>
    #include<stdlib.h>
    #include<crtdbg.h>
    #define NUM 10
    
    int main()
    {
        char *test;
        test = (char*)malloc(NUM * sizeof(char));
        _CrtDumpMemoryLeaks();
        return 0;
    }
    
    输出结果

    使用_CRTDBG_MAP_ALLOC时:

    未使用_CRTDBG_MAP_ALLOC时:

    如果将程序中的:

    char *test;
    test = (char*)malloc(NUM * sizeof(char));
    

    替换为:

    char* name = new char[10];
    

    也会得到同样的结果(内存位置由于自动分配有所不同):

    程序从多个位置退出时:

    1.同样包含上述头文件

    2.在每个会退出程序的函数(包括main函数)开始处包含以下代码:

    _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    

    程序会在退出时自动调用_CrtDumpMemoryLeaks();

    测试代码
    #define _CRTDBG_MAP_ALLOC
    #include<stdio.h>
    #include<stdlib.h>
    #include<crtdbg.h>
    #define NUM 10
    
    void test1();
    void test2();
    
    int main()
    {
        _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
        char *test;
        test = (char*)malloc(NUM * sizeof(char));
        int a;
        puts("input a number");
        scanf_s("%d", &a, sizeof(int));
        if (a > 10)
            test1();
        else if(a<20)
            test2();
        else
            puts("exit point 3");
        return 0;
    }
    
    void test1()
    {
        _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
        puts("exit point 1");
        exit(EXIT_SUCCESS);
    }
    
    void test2()
    {
        _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
        puts("exit point 2");
        exit(EXIT_SUCCESS);
    }
    
    输出结果

    定位内存泄露

    当程序不大时,内存泄露位置可以轻易通过肉眼分辨或者代码审计发现。但是当程序较大的时候,就难以发现具体发生内存泄露的位置。此时可以通过CRT库的_CrtMenState结构储存内存状态。

    使用该结构类型需要定义变量用于储存内存状态:

    _CrtMenState s1,s2,s3;
    

    检测指定位置内存需要用到_CrtMenCheckPoint结构:

    _CrtMenCheckPoint(&s1);
    

    该语句将当前位置的内存状态传递到_CrtMenState结构变量s1中。

    检测两个位置之间的内存状态需要用到_CrtMenDifference()函数,同时需要一个新的_CrtMenState结构变量用于存储状态之间的差异:

    _CrtMemCheckpoint( &s1 );
    _CrtMemCheckpoint( &s2 );
    _CrtMemDifference( &s3, &s1, &s2);
    

    转储_CrtMenState结构的内容需要用到_CrtMemDumpStatistics函数:

    _CrtMenDumpStatics(&s3);
    
    测试代码
    #define _CRTDBG_MAP_ALLOC
    #include<stdio.h>
    #include<stdlib.h>
    #include<crtdbg.h>
    #define NUM 10
    
    _CrtMemState s1, s2, s3;
    void MEM(char *str, int n);
    
    int main()
    {
        _CrtMemCheckpoint(&s1);
        char *test=NULL;
        MEM(test, NUM);
        _CrtMemCheckpoint(&s2);
        puts("test
    ");
        _CrtMemDifference(&s3, &s1, &s2);
        _CrtMemDumpStatistics(&s3);
        return 0;
    }
    
    void MEM(char *str, int n)
    {
        str = (char*)malloc(n * sizeof(char));
    }
    
    输出结果

    当s1和s2之间调用了函数MEN()时:

    当删去程序中的

    MEN(test, NUM);
    

    即s1和s2之间没有调用MEN()函数时:

    如果单纯使用_CrtDumpMemoryLeaks();,只能显示内存分配的位置,即函数MEN()中分配内存语句的位置,而不能定位到是哪一次对函数MEN()调用导致的内存泄露,而通过结构_CrtMenState,可以得到语句之间是否存在内存分配,从而定位出内存泄露的位置。

    总结

    内存泄露的原因很简单,无非就是分配了内存而没有释放,为了解决这个问题,除了养成良好的习惯,还有要善用工具在编写程序的时候实时监测是否出现内存泄露,减少后期排bug时的工作量。

    ============ End

  • 相关阅读:
    ResourceBundle读取utf-8格式properties 中文乱码
    jquery checkbox选中
    扩展RBAC用户角色权限设计方案<转>
    Java调用doNet webService方法
    Mybatis批量更新<转>
    Json转list,两种包,两种方式
    win8.1 64位安装oracle10g客户端心得
    关于JXL读写以及修改EXCEL文件<转>
    Oracle主表列表上显示从表字段拼成的字符串
    ExtJS获取Grid的行数
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/12841132.html
Copyright © 2020-2023  润新知