From: http://blog.sina.com.cn/s/blog_4087d260010090sf.html
一.环境
操作系统:Windows2003 server
IDE:vs2003
二.现象
新建一个dll的空项目,加入mydll.h和mydll.cpp两个文件,内容如下:
// mydll.h
#ifndef _MYDLL_H_
#define _MYDLL_H_
__declspec( dllexport ) void VoidFunc();
#endif
// mydll.cpp
#include "mydll.h"
#include <string>
const std::string description = "Dll:this is my dll";
void VoidFunc()
{
}
采用“多线程调试 DLL”编译,生成mydll.dll和mydll.lib。
新建一个MFC工程,使用“在共享 DLL 中使用 MFC”,导入mydll.lib,可在某处(例如OnButtonClick等)调用VoidFunc。使用DEBUG版本编译运行,在窗口退出时在IDE的输出窗口可看到:
Detected memory leaks!
Dumping objects ->
{112} normal block at 0x00365C78, 32 bytes long.
Data: <Dll:this is my d> 44 6C 6C 3A 74 68 69 73 20 69 73 20 6D 79 20 64
Object dump complete.
很明显,输出表明有内存泄漏。
三.分析
根据IDE输出内容看,在内存地址为0x00365C78有32字节的内存泄漏,内容为:Dll:this is my d...,从内容可以看出,这是mydll中全局string变量description的值。
MFC在DEBUG版本中使用_malloc_dbg和_free_dbg进行分配和释放内存,__malloc_dbg将调用_heap_alloc_dbg进行内存分配,它将一些附加信息(大部分就是dump出来的信息)放在分配内存的前面(其实后面也有),所有分配的内存组成一个双向链表。相应的,_free_dbg将释放内存同时在链表中删除该节点。程序退出之前MFC检查这个链表就可查出内存泄漏情况。这里的细节可查看CRT源码的dbgheap.c文件。
同样,vc71携带的std::string实现的DEBUG版本也是使用_malloc_dbg和_free_dbg进行分配和释放内存,因此MFC是可以跟踪到mydll的内存分配的。难道dll的全局变量description确实没有释放内存吗?
在std::string::~string处设置断点调试,发现description有释放内存,MFC却报内存泄漏,这是怎么回事?
想起MFC是使用CRT函数_CrtDumpMemoryLeaks(dbgheap.c)来报告内存泄漏的,在该处设置断点,在调用堆栈回溯函数调用,发现是函数_DllMainCRTStartup被调用,该函数的参数dwReason为DLL_PROCESS_DETACH,表示这是在卸载mfc71d.dll,继续运行,程序停在std::string::~string断点处,跟踪可知这是字符串变量description。由此可说明:由于mfc71d.dll在my.dll的全局变量释放之前被卸载导致输出内存泄漏报告,但实际上并没有内存泄漏。可将VoidFunc函数中的teststr变量改为static,也可看到报告该变量内存泄漏。
四.结论
由于mfc71d.dll在其他动态库卸载之前被卸载导致输出内存泄漏报告,但实际上并没有内存泄漏。
五.也许没有结束
mfc71d.dll为什么在my.dll的全局变量释放之前(也就是在my.dll卸载之前)被卸载?隐式链接的动态库的卸载顺序的依据是什么,根据代码调用方式还是动态库依赖关系,还是其他什么?