小心DLL链接静态库时的内存错误
最近寫的模塊,在獨立的應用程序中測試是沒問題的,但把它裝配成DLL后,再在另一個應用程序中調用時卻出現了內存錯誤。程序的模塊鏈接關系大概是這樣的:
module就是我所寫的模塊,在這里被封裝為DLL,因為要使用json相關的功能,該DLL鏈接了一個靜態庫 (jsoncpp.lib)。最后在應用程序中導入并使用module.dll,同時因為在應用程序中也需要用到json,所以應用程序也鏈接了jsoncpp.lib。
以下用一些偽代碼來描述這些模塊間的調用關系,以具現出這個錯誤。
jsoncpp.lib為c++提供了功能齊全的json操作,其核心的類是Json::Value。(閱讀本篇文章你無需了解太多json)
module.dll中導出了一個接口:
//ModuleClass.h #include "json/value.h"#if defined MODULE_EXPORTS #define MODULE_EXPORTS __declspec(dllexport) #else #define MODULE_EXPORTS __declspec(dllimport) #endifclass ModuleClass { public:MODULE_EXPORTS void AllocSomeMemory( Json::Value &root ){// 這將申請一些內存,因為會new出一個Json::Value,并append到root上root.append( "testString" );} };應用程序:
| 1 | #include "json/value.h" | 
| 2 | #include "ModuleClass.h" | 
| 3 | int?main() | 
| 4 | { | 
| 5 | ????Json::Value root; | 
| 6 | ????ModuleClass::AllocSomeMemory( root ); | 
| 7 | } | 
在Debug模式下,當main函數執行完畢,對Json::Value root進行析構時,程序便出現了異常。分析下,很顯然,調用ModuleClass::MallocMemoryHere時申請的內存,是在module.dll中申請的,而對這些內存的析構則是在應用程序(.exe)中進行的(析構root會同時析構append在root上的所有子Json::Value)。不過,這是異常的真正原因么?
追蹤到異常的出錯點:dbgheap.c文件中那句ASSERT語句。
| 1 | /* | 
| 2 | * If this ASSERT fails, a bad pointer has been passed in. It may be | 
| 3 | * totally bogus, or it may have been allocated from another heap. | 
| 4 | * The pointer MUST come from the 'local' heap. | 
| 5 | */ | 
| 6 | _ASSERTE(_CrtIsValidHeapPointer(pUserData)); | 
注釋中的最后一句話”The pointer MUST?
come from the ‘local’ heap“引起了我的警惕,難道對于內存的申請和釋放不是在同一個heap上,除了‘local’ heap還有一個什么heap么。
去MSDN上搜索了關于_CrtIsValidHeapPointer,似乎找到了答案,以下這段話是MSDN上對于_CrtIsValidHeapPointer的介紹:
The _CrtIsValidHeapPointer function is used to ensure that a specific memory address is within the local heap. The local heap refers to the heap created and managed by a particular instance of the C run-time library.?If a dynamic-link library (DLL) contains a static link to the run-time library, it has its own instance of the run-time heap, and therefore its own heap, independent of the application’s local heap.?When _DEBUG is not defined, calls to _CrtIsValidHeapPointer are removed during preprocessing.
注意字體加粗的部分,這不正應對我的情形么?!錯誤不在于DLL中申請的內存在EXE中釋放,而在于如果這個DLL擁有一個靜態鏈接,它就會擁有獨立的運行時堆,獨立于應用程序的堆。這樣對于內存申請和釋放并不是在同一個堆上進行的,當然出錯了。
解決:雖然MSDN上最后說,如果把項目改成release的,這個ASSERT就將避免,但這是放縱內存泄露,最好的解決辦法是將靜態鏈接也改成動態鏈接,這樣就使得DLL能夠和應用程序共享同一個堆,錯誤也得以避免。
于是,我修改了jsoncpp的項目配置,生成jsoncpp的動態鏈接庫,而不是使用靜態庫,重新導入到module.dll中,錯誤解決。
 
 
總結
以上是生活随笔為你收集整理的小心DLL链接静态库时的内存错误的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: Mongo数据库搭建
 - 下一篇: _ASSERTE(_CrtIsValid