VC++中运行时库的链接方式对内存方式的影响- Greenerycn's Blog - 博客园

 

今天遇到一个很奇怪的问题,就是调用函数释放内存时程序崩溃,仔细了解才发现时运行时库链接方式对内存的影响。

现场描述:

A.exe中加载B.dll.  在A.exe中用new申请了一片内存,在B.dll中执行delete导致程序崩溃。

原因:

跨模块内存管理不一致导致。A.exe是MD链接,B.dll是MT链接,有2个C++运行库运行在同一个地址空间上,有可能读写同样内存区域,导致程序崩溃。

解决办法:

        每个模块自己管理内存。不要跨模块申请/释放内存.

1. A.exe中调用B.dll中的对应的函数,由其分配内存,然后再调用B.dll中的函数去释放。

2. A申请的内存,由A来释放。

3.B.dll使用MD链接,而且B编译时使用和A编译时相同版本的运行时库。

详解:

在Windows系统中,进程和dll的内存管理是由Runtime Library实现的,而MT和MD影响了Runtim Library的链接方式,从而导致了此问题。

l MT 是多线程静态链接运行时库。

l MD是多线程动态链接运行时库。

在《》中专门讲述了该问题。我这里简单摘录一下:

If you choose to link with the static runtime library, then your module has its own private copy of the C/C++ runtime. When your module calls new or malloc, the memory can only be freed by your module calling delete or free. If another module calls delete or free, that will use the C/C++ runtime of that other module which is not the same as yours. Indeed, even if you choose to link with the DLL version of the C/C++ runtime library, you still have to agree which version of the C/C++ runtime to use. If your DLL uses MSVCRT20.DLL to allocate memory, then anybody who wants to free that memory must also use MSVCRT20.DLL.

另外在MSDN《》也提到:

If you do choose to mix CRT libraries, remember that you have two separate copies of the CRT, with separate and distinct states, so you must be careful about what you try to do across a CRT-boundary. There are many ways to get into trouble with two CRTs. Here are just a few:

  • There are two separate heaps. You cannot allocate (explicitly with new, malloc, or so on -- or implicitly with strdup, strstreambuf::str, or so on), and then pass the pointer across a CRT-boundary to be freed.
  • You cannot pass a FILE* or file handle across a CRT-boundary and expect the "stdio low-level IO" to work.
  • You cannot set the locale in one and expect the other's locale to be set.

简单点说,当使用静态库链接时,会有多份运行时库,而且每份库都拷贝一份自己的内存管理。而使用动态链接后,由于都是链接的同一个运行时库,这样就保证内存管理只用一份了。但是动态链接时必须两个模块使用同样版本的运行时库,不同版本的依旧会有多份。

在该案例中,由于A.exe是MD动态链接,B.dll是MT静态链接,导致有2份运行时库存在,在进行内存管理时就出错了,从而导致程序崩溃。

所以跨模块的内存管理,{zh0}是由每个模块提供自己的分配和销毁接口函数,然后在模块外部通过这些接口的调用来控制对象的生命期,而不是在外部 new/delete。如果实在要用,可以使用微软提供了GlobalAlloc/GlobalFree这样的全局内存API,用它们的话跨模块也没有问题的。

扩展阅读:

1.

郑重声明:资讯 【VC++中运行时库的链接方式对内存方式的影响- Greenerycn's Blog - 博客园】由 发布,版权归原作者及其所在单位,其原创性以及文中陈述文字和内容未经(企业库qiyeku.com)证实,请读者仅作参考,并请自行核实相关内容。若本文有侵犯到您的版权, 请你提供相关证明及申请并与我们联系(qiyeku # qq.com)或【在线投诉】,我们审核后将会尽快处理。
—— 相关资讯 ——