zoukankan      html  css  js  c++  java
  • 基于 crt debug 实现的 Windows 程序内存泄漏检测工具


      Windows 程序内存泄漏检测是一项十分重要的工作,基于 GUI 的应用程序通常在调试结束时也有内存泄漏报告,但这个报告的信息不全面,不能定位到产生泄漏的具体行号。其实自己实现一个内存泄漏检测工具是一件非常简单的事情,但看过网上写的很多例子,普遍存在两种问题:

      1. 要么考虑不周全,一种环境下能用,而在另外一种环境下却不能很好工作,或者漏洞报告的输出方式不合理。
      2. 要么过于保守,例如:完全没有必要在 _malloc_dbg() 和 _free_dbg() 的调用前后用 CriticalSection 进行保护(跟踪一下多线程环境下 new 和 malloc 的代码就会明白)。

      内存检测主要用到以下几个 API,这些 API 能跟踪 new 和 malloc 系列方法申请的内存,具体说明参考帮助文档:

    复制代码
    struct _CrtMemState;
    
    _CrtSetDbgFlag();
    _CrtMemCheckpoint();
    _CrtMemCheckpoint();
    _CrtMemDifference();
    _CrtMemDumpStatistics();
    _malloc_dbg();
    _free_dbg();
    复制代码
    •   头文件:win32_crtdbg.h
    复制代码
    #pragma once
    
    #if defined _DEBUG && defined _DETECT_MEMORY_LEAK
    
    #ifdef new
        #undef new
    #endif
    
    #ifdef delete
        #undef delete
    #endif
    
    #ifndef _CRTDBG_MAP_ALLOC
        #define _CRTDBG_MAP_ALLOC
    #endif
    
    #include <crtdbg.h>
    
    namespace __dbg_impl
    {
        class CDebugEnv
        {
        public:
            CDebugEnv()
            {
                ::_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
                ::_CrtMemCheckpoint(&s1);
            }
    
            ~CDebugEnv()
            {
                ::_CrtMemCheckpoint(&s2);
    
                if (::_CrtMemDifference( &s3, &s1, &s2))
                {
                    TRACE0("!! Memory stats !!
    ");
                    TRACE0("----------------------------------------
    ");
                    ::_CrtMemDumpStatistics(&s3);
                    TRACE0("----------------------------------------
    ");
                }
            }
    
        private:
            _CrtMemState s1, s2, s3;
        };
    
        static __dbg_impl::CDebugEnv __dbgEnv;
    }
    
    inline void* __cdecl operator new(size_t nSize, const char* lpszFileName, int nLine)
    {
        return ::_malloc_dbg(nSize, _NORMAL_BLOCK, lpszFileName, nLine);
    }
    
    inline void* __cdecl operator new[](size_t nSize, const char* lpszFileName, int nLine)
    {
        return operator new(nSize, lpszFileName, nLine);
    }
    
    inline void* __cdecl operator new(size_t nSize)
    {
        return operator new(nSize, __FILE__, __LINE__);
    }
    
    inline void* __cdecl operator new[](size_t nSize)
    {
        return operator new(nSize, __FILE__, __LINE__);
    }
    
    inline void* __cdecl operator new(size_t nSize, const std::nothrow_t&)
    {
        return operator new(nSize, __FILE__, __LINE__);
    }
    
    inline void* __cdecl operator new[](size_t nSize, const std::nothrow_t&)
    {
        return operator new(nSize, __FILE__, __LINE__);
    }
    
    inline void __cdecl operator delete(void* p)
    {
        ::_free_dbg(p, _NORMAL_BLOCK);
    }
    
    inline void __cdecl operator delete[](void* p)
    {
        operator delete(p);
    }
    
    inline void __cdecl operator delete(void* p, const char* lpszFileName, int nLine)
    {
        operator delete(p);
    }
    
    inline void __cdecl operator delete[](void* p, const char* lpszFileName, int nLine)
    {
        operator delete(p);
    }
    
    inline void __cdecl operator delete(void *p, const std::nothrow_t&)
    {
        operator delete(p);
    }
    
    inline void __cdecl operator delete[](void *p, const std::nothrow_t&)
    {
        operator delete(p);
    }
    
    #define new new(__FILE__, __LINE__)
    
    #endif // _DEBUG && defined _DETECT_MEMORY_LEAK
    复制代码
    •   实现文件:win32_crtdbg.cpp
    复制代码
    #include "stdafx.h"
    #include "win32_crtdbg.h"
    
    #if defined _DEBUG && defined _DETECT_MEMORY_LEAK
    
    __dbg_impl::CDebugEnv __dbgEnv;
    
    #endif // _DEBUG && defined _DETECT_MEMORY_LEAK
    复制代码
    • 使用方法
    1. 在 stdafx.h 或其他公共头文件中: #define_DETECT_MEMORY_LEAK#include"win32_crtdbg.h"
    2. 删除项目工程模板中自动生成的 new 操作符重定义,通常自动生成的 cpp 文件在 DEBUG 环境下会把 new 重定义为 DEBUG_NEW。
    • 存在问题

        对于某些全局变量指向的堆内存,如果 ~CDebugEnv() 被调用之时还没释放,则可能存在误报现象。这是一个老大难问题了,目前还没有完美的解决方法。

  • 相关阅读:
    Vue 中的无状态组件
    如何在 Vue 中使用 JSX 以及使用它的原因
    webpack打包优化的四种方法(多进程打包,多进程压缩,资源 CDN,动态 polyfill)
    watch监听对象
    微信小程序动态设置图片大小
    Flutter的生命周期和路由
    两个字符串的编辑距离学习[转载]
    系统进化树怎么看[转载]
    感知机PLA算法实现[转载]
    余弦相似度计算[转载]
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318617.html
Copyright © 2011-2022 走看看