前面的Windows内存体系(6)--跨模块内存分配释放文章解释了跨 MT 模块分配的内存相互释放为什么会崩溃的问题,本文介绍如何解决该问题。
一、问题描述
这篇文章主要介绍我们在实际开发中经常遇到的一个问题:
我们开发了一个DLL,该DLL的运行时库采用MT模式,在为该模块定义接口函数时,接口参数使用的是std::string
类型。EXE程序调用DLL中的接口时遇到"Debug Assertioni Failed"
错误的问题。
上面的错误提示表明触发了debug_heap.cpp
文件中的一个调试断言(release模式
下调用的是heap.cpp
中的分配函数),该断言用于判断指针是否指向堆分配的内存块的第一块。在 release 模式下不会弹出这样的断言错误,程序可能会直接崩溃(崩溃相对来说还比较好排查),就怕出现其他不可预料的、难以排查的错误。
二、实例
现有DLLUser.exe
调用DLL.dll
中的TestFun
函数,代码量非常小:
DLL.dll
中TestFun
函数定义:
1 | DLL_API void TestFun( std::string str) |
DLLUser.exe
中调用TestFun
函数:
1 | int _tmain(int argc, _TCHAR* argv[]) |
上面的代码运行之后程序就会弹出错误断言。原因是std::string
在进行值传参
的过程中会执行一次深拷贝,即:在堆上分配内存块,拷贝“test”到内存块中,然后将临时形参std::string
对象传递到 dll 中,dll 中的TestFun
函数在作用域结束后对临时形参进行释放时就出现了错误,因为尝试在dll的crt堆
中释放由在exe的crt堆
中分配的内存块。
三、自定义std::allocator
通过上面问题的分析,加上前面几篇文章对 Windows 内存体系的介绍,我们不难想出解决方案,其中一种方案就是:让std::string
统一在进程的默认堆上分配内存块,而不是在各个模块的crt堆
上分配。
1 |
|
上面的代码使用自定义的内存分配器vm_allocator<char>
定义了mystring
类,我们只需要将TestFun
函数接口中的std::string
修改为mystring
即可解决崩溃问题。
文章图片带有“CSDN”水印的说明:
由于该文章和图片最初发表在我的CSDN 博客中,因此图片被 CSDN 自动添加了水印。