zoukankan      html  css  js  c++  java
  • windows如何下防止内存泄露

    在windows下开发C++程序的时候,我们经常需要用到malloc开申请内存,然后利用free回收内存,但是开发人员的不小心可能会忘记free掉内存,这样就导致了内存泄露

    1.利用库检测内存泄露信息

    #define _CRTDBG_MAP_ALLOC  //如果没有这个宏定义,我们只能知道有内存泄露,却无法知道在哪个地方申请内存忘记了释放
    #include <stdlib.h>
    #include <crtdbg.h>
    
    int main(void)
    {
        char *p = (char *)malloc(sizeof(char) * 100);
        _CrtDumpMemoryLeaks();
    }

    使用crtdbg来检测到内存泄露很简单,只要在文件的第一行定义_CRTDBG_MAP_ALLOC,然后include头文件crtdbg.h,在程序需要内存检测的地方调用_CrtDumpMemoryLeaks,就可以输出内存泄露的信息,如上面的程序,我们申请了100个字节的内存而没有释放,但是我们可以很清楚地看到内存泄露在 哪个地方,如下图

    我们在main.cpp这个文件中的第八行申请了内存,但是没有进行释放

    那么编译器是怎么知道我们有内存泄露呢??就是利用宏定义把我们的调用的malloc替换成crtdbg 库里面的_malloc_dbg,我们在申请内存的时候,_malloc_dbg会先记录下我们申请内存的行数以及大小(记得编译器有内置的宏定义__LINE__和__FILE__不?),把这些信息放到一个list(只是举例,使用list保存这些信息一旦程序大了会很慢)里面,当我们free内存的时候,把这块内存的信息从list里面删除掉,我们调用_CrtDumpMemoryLeaks()的时候就是在把这个list的信息依次打印出来而已

    下面是我们定义_CRTDBG_MAP_ALLOC后实际上所调用的malloc原型,malloc已经成了一个宏定义

    #define   malloc(s)             _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)

    当然,我们一般调用_CrtDumpMemoryLeaks的时候都是在程序的结尾处,如果我们的程序有多个出口,我们值需要在程序开始处调用_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF ) 就可以了

    有时候我们需要检测某一段代码是否有内存泄露,crtdbg库也可以帮我们做到

        _CrtMemState s1;
        _CrtMemState s2;
    
        _CrtMemCheckpoint(&s1);
    
        char *p2 = (char *)malloc(400);
        _CrtMemCheckpoint(&s2);
        
        _CrtMemState s3;
        if (_CrtMemDifference(&s3,&s1,&s2))
        {
            _CrtMemDumpStatistics(&s3);
        }

    这样,我们在输出窗口将会看到s1和s2之间的内存使用信息:

    0 bytes in 0 Free Blocks.
    400 bytes in 1 Normal Blocks.
    0 bytes in 0 CRT Blocks.
    0 bytes in 0 Ignore Blocks.
    0 bytes in 0 Client Blocks.
    Largest number used: 0 bytes.
    Total allocations: 400 bytes.

    crtdbg库也有缺点,当你使用一个别人提供的lib或者dll库的时候,你调用这个函数,这个函数分配了内存,需要你去调用另外一个函数才能把内存释放掉,但是你不知道这个函数需要调用另外一个函数才能释放掉内存,这个是无法通过crtdbg库检测出来的,这个函数包括c++的new函数,所以这个库实际上不适用C++

    2.利用share_ptr来管理内存

    如果有使用过boost库的应该知道,boost里面有一个shart_ptr被誉为神器,因为他可以帮我们自动管理内存,具体用法很简单:

      boost::shared_ptr < connection > p ( new connection());

    这样的话我们不需要去delete内存,shartd_ptr会在我们不需要这快内存的时候帮我们delete掉,shartd_ptr内部是使用引用计数以及C++的RAII,有别的对象引用该指针的时候引用技术就+1,shartd_ptr析构函数调用的时候引用计数就-1,当为0的时候就delete掉该指针,所以我们并不需要调用delete来释放资源,share_ptr会帮我们管理

    shared_ptr虽然看起来很好用,但是当程序一旦复杂起来,shared_ptr依然也会变复杂(shared_ptr四宗罪),当然boost本身就比较复杂,这个也是我比较不喜欢boost的一个原因

    3.将资源集中管理

    这个也是我比较经常使用的方法,特别是在大程序的使用,配合单件模式,将资源在整个程序或者模块中集中管理,这样在程序结束的时候只要我们在析构函数里面有清理这些资源,我们就可以避免内存泄露,对于数据的一些写操作全部在这个类中统一操作,如果要暴露内部的数据,只对外提供const数据(可以通过强转去掉const属性)

    当然这个方法并不适用于所有场景,比如我们需要提供库给别人,我们没办法预测到客户需要什么操作,所以这个方法只适用内部团队开发

    总之内存管理据我所知到现在还是没有什么好的解决方法,特别是当代码一旦膨胀的时候,到现在好像java,python,erlang都有内存泄露的问题,我们只能在平常开发中多注意了

    参考资料:

    陈硕的博客(有一些shared_ptr的资料,也可以从这里看出shared_ptr使用起来没那么简单)

    shared_ptr四宗罪

    MSDN crtdbg库

  • 相关阅读:
    python
    springboot-mybatis-pagehelper(分页插件)
    图片验证码工具类
    http工具类
    page工具类
    生成count位随机数工具类
    日期工具类
    dozer工具类
    list自定义排序工具类
    fastJson工具类
  • 原文地址:https://www.cnblogs.com/linyilong3/p/2977247.html
Copyright © 2011-2022 走看看