zoukankan      html  css  js  c++  java
  • 重载全局new/delete实现内存检测

    下面介绍用重载new/delete运算符的方式来实现一个简单的内存泄露检测工具,基本思想是重载全局new/delete运算符,被检测代码调用new和delete运算符时就会调用重载过的operator new和operator delete,在重载的operator new里和operator delete里记录下内存申请和释放信息,从而判断内存使用情况。
    下面一步步介绍它的实现!

    1、全局new/delete的重载
        先看一下重载new/delete的规则:
            重载的operator new的参数个数任意,但第一个参数必须是size_t类型的,返回值必须是void*。重载operator delete只允许有一个参数,且是void*型。
        当然,不光要重载operator new 和 operator delete, 还要重载operator new [], operator delete [],更多operator new和operator delete重载的内容参考《Effective C++》
        重载的new/delete, new[]/delete[]代码如下:
        void * operator new (size_t size)
        {
            if(0 == size)
            {
                return 0;
            }
            void *p = malloc(size);
            return p;
        }
     
        void * operator new [](size_t size)
        {
            return operator new(size);
        }
     
        void operator delete (void * pointer)
        {
            if(0 != pointer)
            {
                free(pointer);
            }
        }
     
        void operator delete[](void * pointer)
        {
            operator delete(pointer);
        }
     
    2、用__FILE__, __LINE__记录new的位置
        为了找到内存泄露的元凶,我要记录下每一处new所在的文件名和所在行。于是再次重载了operator new:
        void * operator new (size_t size, const char* file, const size_t line);
        void * operator new [](size_t size, const char* file, const size_t line);
        为了避免编译时出现warning C4291(没有与operator new(unsigned int,const char *,const unsigned int) 匹配的delete),又重载了
        void operator delete (void * pointer, const char* file, const size_t line);
        void operator delete[](void * pointer, const char* file, const size_t line);
        尽管我知道它没用①。
            
        我想到了用系统提供的__FILE__和 __LINE__宏获取当前文件名与行号,我试图把__FILE__和 __LINE__直接填到operator new和operator new[]函数体里边,然后把函数置成inline,结果都输出的是重载operator new和operator new[]的文件和函数体printf函数所在行。然后又试了将operator new的缺省参数设为__FILE__和 __LINE__结果依然,于是想到了用宏定义。

        先看看MFC里的做法,MFC为了调试方便,对new进行了宏定义:
        #define new DEBUG_NEW
        #define DEBUG_NEW new(THIS_FILE, __LINE__)
         
        这里我借用MFC的做法,我也用宏定义:
        void * operator new (size_t size, const char* file, const size_t line);
        void * operator new [](size_t size, const char* file, const size_t line);
        #define MC_NEW new(__FILE__, __LINE__)
        #define new MC_NEW
     
     
    3、将malloc/free 用new/delete替换
        为了便于统计malloc/free信息,也用宏定义的方法处理:
        #define malloc(s) ((void*)new unsigned char[s])
        #define free(p)   (delete [] (char*)(p));
     
    4、在数据结构里存储内存使用情况。
        下面写一个用于存储new/delete中内存信息的数据结构,可以使用链表,也可以使用哈希表,这里选用哈希表,写一个CHash类。
    代码略。
     
    5、为了保证CHash在所有对象析构执行完之后再销毁,应该将CHash放在全局存储区,将其设成static类型,另外,如果有多个static,还需要注意放置的顺序。
     
    到这里这个简易的内存泄露检测工具完成了,但目前还不能用于多线程。


    ①当两个operator new和operator delete有相等的参数个数,并且除了第一个参数之外其余参数的类型依次完全相同之时,我们称它们为一对匹配的operator new和operator delete。
      不过正常情况下带多个参数的delete不会被调用,只有在构建一个对象的过程中,构造函数中发生异常,并且这个异常被捕获了,那么此时C++的异常处理机制才会自动用与被使用的operator new匹配的operator delete来释放内存(补充一点:在operator new中抛出异常不会导致这样的动作,因为系统认为这标志着内存分配失败)。
      试验代码:
        #include <malloc.h>
        struct Base
        {
            Base()
            {
                throw int(3);
            }
            ~Base() {}
            void* operator new( size_t nSize, const char*,int)
            {
                void* p = malloc( nSize );
                return p;
            }
            void operator delete( void *p)
            {
                free(p);
            }
            void operator delete( void* p,const char*,int)
            {
                free( p );
            }
        };
        #define new new(__FILE__, __LINE__)
        int main( void )
        {
            Base* p = null;
            try
            {
                p = new Base;
                delete p;
            }
            catch(...)
            {
            }
            return 0;
        }

  • 相关阅读:
    oracle 视图views
    5分钟把任意网站变成桌面软件
    Angular http跨域
    jQuery版本升级踩坑大全
    redis 模糊删除key
    jedisCluster 报错: redis.clients.jedis.exceptions.JedisClusterException: No way to dispatch this command to Redis Cluster because keys have different slots.
    mac电脑复制键失灵
    java8 for ,forEach ,lambda forEach , strean forEach , parller stream forEach, Iterator性能对比
    linux光标操作
    redis hashmap数据结构分析
  • 原文地址:https://www.cnblogs.com/dongzhiquan/p/3310885.html
Copyright © 2011-2022 走看看