当前位置: 首页 > news >正文

C++跨DLL内存所有权问题探幽(一)DLL提供的全局单例模式

最近在开发的时候,特别是遇到关于跨DLL申请对象、指针、内存等问题的时候遇到了这么一个问题。

问题 跨DLL能不能调用到DLL中提供的单例?

问题比较简单,就是我现在有一个进程A,有DLL B DLL C,这两个DLL都依赖DLL D的单例,这个时候如果A调用了DLLB 和 DLL C,那么B和C能否正确引用到这个指定的单例?

其实这个问题我心中想的是可以使用这个进程的,但是在实际开发中我们却有了争议,有的人认为可以,有的人认为不可以,但是我对这个内存的所有权一直不太清楚,所以这也算是给自己一个解惑,一个交代。这篇文章也是我边查资料边写的,有引用很多文章,主要是为了引出结论和一些别的问题。

聊C/C++的动态内存管理的内容之前,我们先来了解一下进程中的内存分区,如上所示。我们可以通过查看程序的完整启动过程去看这些内存分区。

内存分区

在C++中,将操作系统分配给程序的内存空间按照用途划分了代码段、数据段、栈、堆几个不同的区域,每个区域都有其独特的内存管理机制。

代码区

只存代码本身,几乎不需要考虑

代码区是用于存储程序代码的区域,代码段在程序真正执行前就被加载到内存中,在程序执行期间,代码区内存不会被修改和释放。由于代码区是只读的,所以会被多个进程共享。在多个进程同时执行同一个程序时,操作系统只需要将代码段加载到内存中一次,然后让多个进程共享这个内存区域即可。

数据段

纯粹的数据,几乎不用考虑

数据段用于存储静态全局变量、静态局部变量和静态常量等静态数据。在程序运行期间,数据段的大小固定不变,但其内容可以被修改。按照变量是否被初始化。数据段可分为已初始化数据段和未初始化数据段。

存放局部变量和函数调用。局部变量访问出错和函数死循环往往会导致stacks overflow。

C++中函数调用以及函数内的局部变量的使用,都是通过栈这个内存分区实现的。栈分区由操作系统自动分配和释放,是一种"后进先出"的一种内存分区。每个栈的大小是固定的,一般只有几MB,所以如果栈变量太大,或者函数调用嵌套太深,容易发生栈溢出(stack overflow)。

一般就是我们new出来的指针,是一些内存空间。当内存地址所有权不明或者内存出错就容易出现堆损坏问题。

用于存放在程序运行时被动态分配的内存段。堆的大小不固定,可以动态增加和减少。使用malloc()等函数动态分配内存到堆上,使用free()等函数释放对应的动态分配内存。堆的最大容量受限于系统中有效的虚拟内存。

栈和堆?

区别:

1、申请方式:栈的空间由操作系统自动分配和释放,堆上的空间需要程序员使用malloc\free手动分配和释放。如果不释放会造成内存泄漏。

2、申请大小限制和效率:栈的空间时有限的,在linux中,使用 ulimit -s 指令 ,可以看到看到栈的容量为8M。栈区以先进后出的方式自动分配时连续的内存单元,效率高。

堆的大小受限与系统中有效的虚拟内存大小,系统是用链表来存储空闲的内存块,是不连续的。因此,堆的空间分配比较灵活,但容易产生内存碎片,相对来讲对效率低。

对于一个程序而言,大概的模型是这样的

image

以启动Windows系统中的exe程序为例

当我们双击某个exe程序或者通过桌面等快捷方式去启动一个exe程序时,就进入exe程序的启动过程。

image

程序启动时,系统首先会将exe主程序依赖的所有的dll库文件(包括exe程序自带的dll以及exe程序依赖的系统dll)都加载到进程空间中,这些dll二进制文件中存放的是可执行的二进制机器代码(即汇编代码,机器码与汇编代码等价的,汇编代码是机器码的助记符),都加载到进程的代码段的内存区中。

待所有依赖的dll模块都加载到进程空间后,最后才会将exe主程序加载到进程空间中。然后去启动C/C++的运行时库,紧接着去给全局变量分配内存并执行全局变量的初始化操作,此处对应的就是全局内存区。然后才会进入到main函数,程序才能真正的启动并运行起来。

进入到函数中,就会从所在线程的栈内存上给函数的局部变量分配栈内存,这就是我们讲的栈内存。当执行到malloc或new等代码时,申请的内存就是堆内存。

回到问题

这个问题其实实际上还没有涉及到内存所有权问题,但是我觉得可以当作一个引子:

static变量的唯一性是动态库级别的,不同库包含同一截代码的话就会有多份static实例。出现问题的情况往往是,这截代码是实现在头文件里的,所以被不同模块引用时就生成了多份代码。解决办法就是按照一般DLL导出接口的规则,在获取static变量的地方:

1.对于实现单例的dll,使用dllexport。对于MSVC来说就是__declspec(dllexport),对于GCC/clang就是__attribute__ ((dllexport))

2.对于引用单例的dll,使用dllimport。对于MSVC/clang来说就是__declspec(dllimport),对于GCC/clang就是__attribute__ ((dllimport))这样就只有一份了。

所以跨DLL的单例模式是可行的,这个静态变量(单例对象)必须导出,否则这个静态对象的实现会在不同的代码头文件引用的过程中一同被申请出来。

解决方案:

1.设计代码时选好单例的内存该放在哪,然后通过导入导出一解决(MSVC上导入导出使_declspec(dllimport)和_declspec(dllexport),类比extern变量一)。比如单例打算放在动态链接库A中,那就在生成动态库A的地方的这个static变量导出,其他动态链接库°或可执行文件使用的地方导入。

2.搞个统一的地方大家的单例共同注册到一起,用的时候拿出来。

tips:

Windows使用注意事项:

  1. 要求使用MD/MDd,而不是MT/MTd,反正至少保证要保证exe和所有dll使用同一间使用同一个crtheap堆,否则exe和不同dll间都有各自的crtheap堆,而new和delete需要在同一堆中配套执行。
  2. 慎用virtual,析构函数不能为virtual函数。因为当析构函数为virtual函数时,如果单例由dll1加载,而dll2在dll1卸载之后如果还在使用,那析构时调virtual的析构函数会去查虚函数表,而虚函数表是由dll1创建的,会引发崩溃。其他虚函数也涉及虚函数表,因此若要使用虚函数,那么除非能保证dll的卸载顺序,否则不要使用热卸载。事实上,全局单例的管理交由单例框架来实现后,析构函数是否使用virtual都不会产生泄漏,因为单例框架构造和析构时使用的都是具体的全局单例类,而不会是它们的基类。
  3. 单例全为懒加载,直到GetReference的时候才真正实例化单例对象,需要注意全局单例没有保证多线程间安全,因此在单例实例化/动态库首次获取单例时都是线程不安全的。若在SingletonManager.cpp的Count/Obtain/Release函数中使用std::mutex加锁能够实现单例获取的安全,但实例化过程(创建过程)仍是线程不安全的。

下一期来聊聊关于跨DLL引用DLL的时候导致的所有权问题

相关文章:

C++跨DLL内存所有权问题探幽(一)DLL提供的全局单例模式

最近在开发的时候,特别是遇到关于跨DLL申请对象、指针、内存等问题的时候遇到了这么一个问题。 问题 跨DLL能不能调用到DLL中提供的单例? 问题比较简单,就是我现在有一个进程A,有DLL B DLL C,这两个DLL都依赖DLL D的…...

短时间不点击云服务器,自动化断开连接,怎么设置长时间

在 Linux 系统中,如果你希望在一段时间内没有操作后保持远程连接不断开,可以通过修改 SSH 服务器的配置来实现。具体的步骤如下: 打开 SSH 服务器的配置文件: sudo vi /etc/ssh/sshd_config 找到以下两个参数并进行修改&#xff…...

typhonjs-escomplex 代码可读性 可维护度探索

目前市面上的前端代码质量评分中的代码可维护度是大都是基于 typhonjs-escomplex 这个库扫描而来,但是这个库的官方文档并没有介绍相关指标数据的计算规则,不知道规则如何提升指标数据呢?所以本文对 typhonjs-escomplex 源码进行探索&#xf…...

支持向量机基本原理,Libsvm工具箱详细介绍,基于支持向量机SVM的人脸朝向识别

目录 支持向量机SVM的详细原理 SVM的定义 SVM理论 Libsvm工具箱详解 简介 参数说明 易错及常见问题 完整代码和数据下载链接: 基于支持向量机SVM人脸朝向识别(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88527821 SVM应用实例, 基…...

密码破解工具的编写

预计更新 网络扫描工具的编写漏洞扫描工具的编写Web渗透测试工具的编写密码破解工具的编写漏洞利用工具的编写拒绝服务攻击工具的编写密码保护工具的编写情报收集工具的编写 密码破解工具是一种常见的安全工具,它可以通过不断尝试不同的密码组合来破解加密的数据或…...

BES2700H开发不完全手册

BES2700H开发不完全手册 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加我微信hezkz17, 本群提供音频技术答疑服务,群赠送语音信号处理降噪算法,ANC AEC ENC EQ BF BES蓝牙耳机音频资料 1 成功编译 2 代码 3 开放文档...

OpenGL的学习之路-3

前面1、2介绍的都是glut编程 下面就进行opengl正是部分啦。 1.绘制点 #include <iostream> #include <GL/gl.h> #include <GL/glu.h> #include <GL/glut.h>void myMainWinDraw();int main(int argc,char** argv) {glutInit(&argc,argv);glutIni…...

Vue 小黑记事本组件版

渲染功能&#xff1a; 1.提供数据&#xff1a; 提供在公共的父组件 App.vue 2.通过父传子&#xff0c;将数据传递给TodoMain 3.利用 v-for渲染 添加功能&#xff1a; 1.收集表单数据 v-model 2.监听事件&#xff08;回车点击都要添加&#xff09; 3.子传父&#xff0c;讲…...

javascript如何清空数组?

可以使用以下方法清空JavaScript数组&#xff1a; 直接赋值为空数组 arr []; let arr [1, 2, 3, 4]; arr []; // 现在arr是空数组使用 splice() 方法删除所有元素 let arr [1, 2, 3, 4]; arr.splice(0, arr.length); // 现在arr是空数组使用 length 属性将数组截断 let ar…...

MySQL MHA高可用切换

MySQL MHA 1&#xff0e;什么是 MHA MHA&#xff08;MasterHigh Availability&#xff09;是一套优秀的MySQL高可用环境下故障切换和主从复制的软件。 MHA 的出现就是解决MySQL 单点的问题。 MySQL故障切换过程中&#xff0c;MHA能做到0-30秒内自动完成故障切换操作。 MHA能在…...

【Python】【应用】Python应用之一行命令搭建http、ftp服务器

&#x1f41a;作者简介&#xff1a;花神庙码农&#xff08;专注于Linux、WLAN、TCP/IP、Python等技术方向&#xff09;&#x1f433;博客主页&#xff1a;花神庙码农 &#xff0c;地址&#xff1a;https://blog.csdn.net/qxhgd&#x1f310;系列专栏&#xff1a;Python应用&…...

C++模拟实现——红黑树

一、介绍 红黑树也是对一般的搜索二叉树不能保证平衡的一个改进&#xff0c;和AVL树采用的思路不同&#xff0c;但同样需要旋转&#xff0c;其本质也是一颗平衡搜索二叉树&#xff0c;其节点有颜色的区分&#xff0c;并且被一些规则束缚&#xff0c;在这些规则下&#xff0c;能…...

java基础-数据类型

1、变量 变量就是申请内存来存储值。也就是说&#xff0c;当创建变量的时候&#xff0c;需要在内存中申请空间。 内存管理系统根据变量的类型为变量分配存储空间&#xff0c;分配的空间只能用来储存该类型数据。 因此&#xff0c;通过定义不同类型的变量&#xff0c;可以在内…...

设计数据库的时候会考虑哪些因素,怎样去建表?

在设计数据库时&#xff0c;通常会考虑以下因素&#xff1a; 数据的结构和关系&#xff1a;首先需要分析业务需求&#xff0c;了解需要存储的数据类型、数据之间的关系以及数据的组织结构。 数据的完整性和一致性&#xff1a;确保数据库中的数据完整性和一致性&#xff0c;例如…...

AI 绘画 | Stable Diffusion精确控制ControlNet扩展插件

ControlNet ControlNet是一个用于控制AI图像生成的插件,通过使用Conditional Generative Adversarial Networks(条件生成对抗网络)的技术来生成图像。它允许用户对生成的图像进行更精细的控制,从而在许多应用场景中非常有用,例如计算机视觉、艺术设计、虚拟现实等。 对于…...

青少年编程学习 等级考试 信奥赛NOI/蓝桥杯/NOC/GESP等比赛资料合集

一、博主愚见 在当今信息技术高速发展的时代&#xff0c;编程已经成为了一种必备的技能。随着社会对于科技人才的需求不断增加&#xff0c;青少年编程学习正逐渐成为一种趋势。为了更好地帮助青少年学习编程&#xff0c;提升他们的技能和素质&#xff0c;博主结合自身多年从事青…...

Linux 函数库

函数库&#xff1a; 我们的C程序中&#xff0c;并没有定义“printf”的函数实现,且在预编译中包含的“stdio.h”中也只有该函数的声明,而没有定义函数的实现,那么,是在哪里实“printf”函数的呢? 最后的答案是:系统把这些函数实现都被做到名为 libc.so.6 的库文件中去…...

Java 入门基础题

目录 1.输出一个整数的每一位 2.判定素数 3.求最大值方法的重载 4.输出闰年 5.打印 X 图形 6.数字9 出现的次数 7.计算分数的值 8. 模拟登陆 9.使用函数求最大值 10.斐波那契数列 星光不负赶路人&#xff0c;加油铁子们&#xff01;&#xff01;&#xff01; 1…...

块设备的工作模式

块设备的mknod 还是会创建在 /dev 路径下面&#xff0c;这一点和字符设备一样。/dev 路径下面是 devtmpfs 文件系统。这是块设备遇到的第一个文件系统。我们会为这个块设备文件&#xff0c;分配一个特殊的 inode&#xff0c;这一点和字符设备也是一样的。只不过字符设备走 S_IS…...

Spring核心

Spring Framework Spring的两个核心IOC控制反转IOC容器依赖注入DIIOC容器实现注解管理BeanBean对象定义Bean对象获取 AOP面向切面编程 添加依赖入门案例注解通过Spring创建Java bean对象 xml管理Bean案例main下创建bean.XMl文件 DI依赖注入案例创建Spring配置文件 bean-di.xml …...

如何优化A-to-Z-Resources-for-Students文档的行距与段距:提升阅读体验的完整指南

如何优化A-to-Z-Resources-for-Students文档的行距与段距&#xff1a;提升阅读体验的完整指南 【免费下载链接】A-to-Z-Resources-for-Students ✅ Curated list of resources for college students 项目地址: https://gitcode.com/GitHub_Trending/at/A-to-Z-Resources-for…...

5分钟掌握MPC Video Renderer:解锁专业级HDR视频渲染的完整解决方案

5分钟掌握MPC Video Renderer&#xff1a;解锁专业级HDR视频渲染的完整解决方案 【免费下载链接】VideoRenderer RTX HDR modded into MPC-VideoRenderer. 项目地址: https://gitcode.com/gh_mirrors/vid/VideoRenderer MPC Video Renderer是一款功能强大的开源DirectSh…...

别再手动传文件了!用MinIO Java SDK的预签名URL功能,5分钟搞定安全文件分享

别再手动传文件了&#xff01;用MinIO Java SDK的预签名URL功能&#xff0c;5分钟搞定安全文件分享 上周团队新来的架构师老张给我看了一个令人后怕的日志&#xff1a;某个内部系统的文件下载接口在24小时内被调用了17万次&#xff0c;而实际业务需求只有不到200次。调查发现是…...

IndexTTS-2-LLM语音合成应用:无障碍辅助与内容创作指南

IndexTTS-2-LLM语音合成应用&#xff1a;无障碍辅助与内容创作指南 1. 语音合成技术概述 1.1 什么是智能语音合成 智能语音合成&#xff08;Text-to-Speech&#xff0c;TTS&#xff09;技术能够将文字信息转换为自然流畅的语音输出。IndexTTS-2-LLM作为新一代语音合成系统&a…...

亚马逊 API 签名认证机制详解

在调用亚马逊开放平台、亚马逊云服务&#xff08;AWS&#xff09;各类 API 时&#xff0c;签名认证是请求合法的核心门槛&#xff0c;目前主流采用 Signature Version 4&#xff08;SigV4&#xff09; 签名机制。它通过对请求内容与密钥做加密计算&#xff0c;实现身份校验、防…...

互联网大厂Java面试实战:严肃面试官与搞笑程序员谢飞机的三轮问答

互联网大厂Java面试实战&#xff1a;严肃面试官与搞笑程序员谢飞机的三轮问答 在互联网大厂Java岗位面试中&#xff0c;面试官不仅考察应聘者的技术深度&#xff0c;更关注其理解业务场景的能力和解决问题的方法。本文通过一场幽默而真实的模拟面试&#xff0c;呈现核心Java与周…...

别再死记硬背了!用Multisim仿真带你玩转计数器与数据选择器(附FPGA引脚配置)

用Multisim仿真与FPGA实战&#xff1a;计数器与数据选择器的设计艺术 数字电路课程中那些抽象的概念&#xff0c;是否曾让你感到困惑&#xff1f;模5计数器、序列信号发生器这些名词听起来高深莫测&#xff0c;但通过Multisim仿真和FPGA实战&#xff0c;你会发现它们其实可以很…...

如何用Chatterbox TTS打造多语言智能语音助手:从零开始的完整实战指南 [特殊字符]

如何用Chatterbox TTS打造多语言智能语音助手&#xff1a;从零开始的完整实战指南 &#x1f3a4; 【免费下载链接】chatterbox Open source TTS model 项目地址: https://gitcode.com/GitHub_Trending/chatterbox7/chatterbox 想要为你的应用添加逼真的语音合成功能吗&a…...

RuoYi-Cloud微服务架构下PostgreSQL数据库迁移实战指南

1. 迁移前的准备工作 在开始将RuoYi-Cloud从MySQL迁移到PostgreSQL之前&#xff0c;我们需要做好充分的准备工作。这就像搬家前要打包物品一样&#xff0c;准备工作做得好&#xff0c;后续的迁移过程就会顺利很多。 首先&#xff0c;我们需要了解PostgreSQL和MySQL之间的主要差…...

SMUDebugTool终极指南:快速掌握AMD Ryzen系统调试与优化技巧

SMUDebugTool终极指南&#xff1a;快速掌握AMD Ryzen系统调试与优化技巧 【免费下载链接】SMUDebugTool A dedicated tool to help write/read various parameters of Ryzen-based systems, such as manual overclock, SMU, PCI, CPUID, MSR and Power Table. 项目地址: http…...