IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> CRT内存检测 -> 正文阅读

[C++知识库]CRT内存检测

除了VLD(Visual Leak Detector)、Dr.Memory这些第三方检测工具之外,还可以用vs自带的CRT内存泄漏检测工具来检测内存泄漏。对于MFC工程来说尤其方便,甚至都不需要再手动添加代码,Debug模式下自动开启了CRT检测。

怎么使用我就不废话了,直接去看官方文档:

使用 CRT 库查找内存泄漏 - Visual Studio (Windows) | Microsoft Docs

值得一提的是,它不仅能用于检测内存泄漏,还能用来检测内存越界。

原理

对于MFC工程,一般在程序主窗口源文件上会有这么一段:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

说白了,CRT检测内存泄漏的方法就是重载了new关键字,每次通过new申请内存,走的都是CRT重载的new方法。重载的new方法最后会走到这个函数:_heap_alloc_dbg_impl

extern "C" static void * __cdecl _heap_alloc_dbg_impl(
        size_t nSize,
        int nBlockUse,
        const char * szFileName,
        int nLine,
        int * errno_tmp
        )
{
    ......
}

?

检测内存泄漏

CRT检测内存泄漏的原理其实很简单,就是使用new申请内存时记录到全局链表中,然后等程序退出时检查链表内还有哪些内存未被释放。

new申请内存,走到_heap_alloc_dbg_impl函数时,会将申请的内存记录到全局链表中:

if (_pFirstBlock)
    _pFirstBlock->pBlockHeaderPrev = pHead;
else
    _pLastBlock = pHead;

在程序退出时,调用_CrtDumpMemoryLeaks检测内存泄漏:

extern "C" _CRTIMP int __cdecl _CrtDumpMemoryLeaks(
        void
        )
{
    /* only dump leaks when there are in fact leaks */
    _CrtMemState msNow;

    _CrtMemCheckpoint(&msNow);

    ......

    return FALSE;   /* no leaked objects */
}

?_CrtMemCheckpoint遍历链表,找出所有引用计数大于0的内存块(即未被释放)

for (pHead = _pFirstBlock; pHead != NULL; pHead = pHead->pBlockHeaderNext)
{
    if (_BLOCK_TYPE(pHead->nBlockUse) >= 0 && _BLOCK_TYPE(pHead->nBlockUse) < _MAX_BLOCKS)
    {
        state->lCounts[_BLOCK_TYPE(pHead->nBlockUse)]++;
        state->lSizes[_BLOCK_TYPE(pHead->nBlockUse)] += pHead->nDataSize;

    }
    
    ......
}

CRT的内存泄漏检测机制不是完美无缺的。CRT应该检测不出SysAllocString导致的内存泄漏,因为SysAllocString的内存是由系统分配的,不是通过new申请的。同理,通过malloc申请的内存,如果内存泄漏了,也无法通过CRT检测出来。

检测写内存越界

这里只说写越界的原理,一句话总结就是:将内存前后填充为特定值(0xFD),如果写内存之后内存前(后)不再是特定值(0xFD)了,就说明写越界了

读越界的判断原理我也没空去看源码,无脑猜测是根据起始地址和内存大小判断。

_heap_alloc_dbg_impl申请内存的时候,并不是直接按申请的大小new一块内存返回就算了,而是加了一些东西:

blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize;

RTCCALLBACK(_RTC_FuncCheckSet_hook,(0));
pHead = (_CrtMemBlockHeader *)_heap_alloc_base(blockSize);

实际申请的内存大小?= sizeof(_CrtMemBlockHeader) + 申请的内存大小 + nNoMansLandSize

也就是说,在内存块前后都加了一点东西,最后判断内存越界靠的就是前后这2块东西。

_CrtMemBlockHeader的结构:

nDataSize记录了用户申请的内存大小,gap是整个结构最后4个字节。

typedef struct _CrtMemBlockHeader
{
        struct _CrtMemBlockHeader * pBlockHeaderNext;
        struct _CrtMemBlockHeader * pBlockHeaderPrev;
        char *                      szFileName;
        int                         nLine;
#ifdef _WIN64
        /* These items are reversed on Win64 to eliminate gaps in the struct
         * and ensure that sizeof(struct)%16 == 0, so 16-byte alignment is
         * maintained in the debug heap.
         */
        int                         nBlockUse;
        size_t                      nDataSize;
#else  /* _WIN64 */
        size_t                      nDataSize;
        int                         nBlockUse;
#endif  /* _WIN64 */
        long                        lRequest;
        unsigned char               gap[nNoMansLandSize];
        /* followed by:
         *  unsigned char           data[nDataSize];
         *  unsigned char           anotherGap[nNoMansLandSize];
         */
} _CrtMemBlockHeader;

nNoMansLandSize的定义:

#define nNoMansLandSize 4

在用户申请的内存块前面(_CrtMemBlockHeader->gap)和后面(nNoMansLandSize),各有4个字节,用0xFD填充:

/* fill in gap before and after real block */
memset((void *)pHead->gap, _bNoMansLandFill, nNoMansLandSize);
memset((void *)(pbData(pHead) + nSize), _bNoMansLandFill, nNoMansLandSize);

那么,如果用户在写内存时越界了,就会覆盖掉前面或者后面4个字节,这一小块内存的内容就不再是0xFD了。因此,我们可以通过判断前后4个字节是否依然是0xFD来判断写内存越界。

if (!CheckBytes(pbData(pHead) + pHead->nDataSize, _bNoMansLandFill,
                nNoMansLandSize))
{
	if (pHead->szFileName)
	{
		_RPT5(_CRT_WARN, "HEAP CORRUPTION DETECTED: after %hs block (#%d) at 0x%p.\n"
			"CRT detected that the application wrote to memory after end of heap buffer.\n"
			_ALLOCATION_FILE_LINENUM,
			blockUse,
			pHead->lRequest,
			(BYTE *) pbData(pHead),
			pHead->szFileName,
			pHead->nLine);
	}
	else
	{
		_RPT3(_CRT_WARN, "HEAP CORRUPTION DETECTED: after %hs block (#%d) at 0x%p.\n"
			"CRT detected that the application wrote to memory after end of heap buffer.\n",
			blockUse, pHead->lRequest, (BYTE *) pbData(pHead));
	}
	okay = FALSE;
}

extern "C" static int __cdecl CheckBytes(
        unsigned char * pb,
        unsigned char bCheck,
        size_t nSize
        )
{
        while (nSize--)
        {
            if (*pb++ != bCheck)
            {
                return FALSE;
            }
        }
        return TRUE;
}

例子 HEAP CORRUPTION DETECTED

这是我遇到过的一个情况,当时还找了好长时间。报错截图:

?

这个报错是必现的,每次都是在delete一个自定义的Image对象时弹框报错。先说报错原因,Image类是dll导出的类,但是头文件和dll对不上(版本原因),导致new出来的对象内存大小不对,进而导致写内存越界。

参考:

“HEAP CORRUPTION DETECTED”错误原因与解决_康康是大神的博客-CSDN博客_heap_corruption

char* p=new char[5];
strcpy(p,"aaaaa");
delete[] p;

申请的内存大小是5个字节,但是写内存的时候写了6个字节(包含一个结束符)。写越界了可能不会立即报错,delete内存的时候,就发现越界了。参考上文:检测写内存越界

当时百思不得其解,我就new一个Image对象,然后delete就报错了?各种查代码,后来跟踪CRT代码偶然发现delete的内存大小是208,但Image对象的实际大小应该是216,内存大小对不上。

顺着查下去发现了原因,这个Image类是一个dll导出的类,但是头文件和dll对不上了。我使用的头文件的版本比较老,实际上dll的Image类新增了2个int成员,导致头文件和dll的Image对象差了8个字节。更新头文件,重新编译,问题解决。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-04-26 11:24:29  更:2022-04-26 11:26:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:14:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码