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()
{
const std::string teststr = "Func:VoidFunc string";
}
採用“多線程調試 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卸載之前)被卸載?隱式鏈接的動態庫的卸載順序的依據是什麼,根據代碼調用方式還是動態庫依賴關係,還是其他什麼?