Advertisement

C++跨DLL内存所有权问题探析(二)CRT中的MT与MD混用导致的堆损坏

阅读量:

0xC0000374: 堆已损坏。 (参数: 0x00007FFA1E9787F0)。
_Mem 是 nullptr

我在开发过程中遇到了上面提到的两个问题(BUG),实在不明白为什么会发生这种情况。经过一番折腾后才明白这是因为这两个DLL中的MT和MD选项设置不一致所导致的。

当遇到某个特定情境时,在一个MT编译生成的DLL A中引用了一段来自MD编译生成的内容,并将其导入到A文件头部分;同时,在A头部文件中标明了B对象的存在

在正式讨论这个问题之前,需要做一些知识储备

什么是MD和MT编译?

在C++开发中,MD(Multi-threaded DLL)和MT(Multi-threaded)作为微软Visual C++编译器所提供的不同运行时库选项,在多线程操作、内存管理以及连接方式等方面存在差异

在C++开发中,MD(Multi-threaded DLL)和MT(Multi-threaded)作为微软Visual C++编译器所提供的不同运行时库选项,在多线程操作、内存管理以及连接方式等方面存在差异

Multi-threaded DLL(MD):

相当于在编译的时候不将DLL依赖的DLL放在其内部。

采用动态链接多线程C/C++运行时库(CRT)配置通常表示为MD选项。这表明该程序会共享所使用的这些CRT库。因此,在这种情况下生成的应用程序可执行文件通常会较小。

运行时常所需的版本通常会根据操作系统来确定;假设您的系统已经预先安装了与当前应用需求匹配的运行时常,则您的应用程序就可以方便地获取所需资源而不必进行额外安装。

Multi-threaded(MT):

相当于在编译的时候将DLL依赖的DLL放在其内部。

MT选项表示您的程序将采用静态链接的多线 thread C/C++ runtime library(CRT)。这表明您的程序将包含完整的runtime library副本,从而可能导致最终生成的可执行文件体积增大。这些runtime libraries会随程序一起分发,因此无需用户在应用前安装任何额外组件.这些runtime libraries负责执行诸如内存管理和线程处理等与C/C++编程相关的工作.它们提供了诸如动态内存分配和释放以及线程同步等功能.选择不同的runtime libraries将根据项目的具体需求以及对最终可执行文件大小和依赖性的影响来决定.不同类型的runtime libraries可能会影响程序的性能和行为.

关于DLL引用

书接上文C++跨DLL内存所有权问题探幽(一)DLL提供的全局单例模式
我们了解任意程序都有内存区域。

在C++编程过程中,堆内存与栈内存是分别用于存储变量和对象的主要内存区域。当某个进程调用DLL(Dynamic Link Library)时,在源文件头部定义对象类型与指针声明之间存在一些本质区别:

  1. 对象声明:

如果在一个头文件中声明一个对象,则该对象会被分配到内存堆栈中。
这表明该对象在其作用域范围内拥有有限的生命期。
一旦该作用域范围结束时,
则该对象会被自动销毁并释放其所占内存的空间。

当一个实体(对象)被定义在动态链接库中时,在使用动态链接库编译生成的程序(或:在构建可执行文件时),该实体的属性和操作方法会被复制到主源代码文件中(或:主程序文件)。这可能导致对同一个实体进行双重定义的风险。

  1. 指针声明:

如果在一个包含头文件的地方定义一个指针变量,则该指针变量将被存储于堆栈内存区域。
然而,在动态链接库(DLL)中使用new关键字创建的对象可能会存储于堆区

通过使用指针,在程序中传递对象的引用而非实际的对象本身。这从而使得对象能够在内存堆上动态地分配内存块,并且能够在不同的模块之间共享数据。

也就是说

为什么崩溃?

当在C++中不当混合使用具有不同线程模型的DLL时,其主要原因是内存操作越界造成的,堆栈空间所有权的问题.

对堆空间而言

当采用 MD 进行 DLL 生成时,在这种情况下,默认情况下会依赖操作系统提供的公共多线程 C/C++ 运行时库来处理内存问题。具体来说,在 MD 编译生成的 DLL 中若预先分配了一块堆内存,则该内存将由系统运行时库负责进行管理和分配。

对于MT编译生成的DLL文件,在使用静态链接方式的情况下采用了多线程式的C/C++运行时库架构,并因此具备了独立实现堆内存管理的功能。当您在该DLL中分配一块动态内存空间时,默认情况下该内存空间将由其运行时库负责管理和分配。

栈空间:

栈空间的所有权属于当前线程。当切换至MD与MT编译的DLL之间时, 栈空间的所有权可能会发生变化。若一个线程先在MD编译的DLL中分配了一块栈内存,随后却试图从MT编译的DLL中释放它,反之亦然,这将导致内存冲突,从而引发不可预测的行为或可能导致程序崩溃。

在混合使用MD和MT编译生成的DLL中存在潜在风险。由于堆栈与栈空间所有权归属及管理策略的不同,在资源分配上可能出现不一致的情况。这种不一致性可能导致内存溢出或资源泄漏等问题。这些异常情况可能引发一系列问题包括但不限于内存泄漏指针越界以及数据损坏等严重后果进而导致程序崩溃或出现不可预测的行为为了避免此类问题发生应当在整个应用开发过程中始终采用统一的运行时库进行DLL构建工作

MD(d)、MT(d)编译选项,当采用Release编译时不会触发这个崩溃问题。

MD(d)、MT(d)编译选项,当采用Release编译时不会触发这个崩溃问题。

全部评论 (0)

还没有任何评论哟~