Windows内存体系(7)--使用std::string跨MT模块传参
前面的Windows内存体系(6)--跨模块内存分配释放文章解释了跨 MT 模块分配的内存相互释放为什么会崩溃的问题,本文介绍如何解决该问题。
前面的Windows内存体系(6)--跨模块内存分配释放文章解释了跨 MT 模块分配的内存相互释放为什么会崩溃的问题,本文介绍如何解决该问题。
在《Windows核心编程 第五版》第19章 DLL基础(511页)中给出了一个建议: “当一个 MT 版本的模块如果提供一个内存分配函数的时候,它必须同时提供另一个用来释放内存的函数。”。 说得更加直白一点就是,“对于 MT 的模块,不要跨模块进行内存释放。”。但是核心编程这本书上面没有具体分析原因,本文就来分析具体的原因。
一、为什么要使用堆 (Heap)?应用程序虽然可以使用页面粒度的函数(如VirualAlloc)来分配一个最小为4KB或8K的内存块,但是很多时候我们并不需要分配这么大的内存块,我们可能只想分配 1K,2K 的内存块,那么这个时候无论从内存的使用率,还是从性能的角度来看,再分配这么大的一个内存区域显然不是最优的了。 为了满足这种需求,Windows 提供了一个被称为“堆管理器”的组件,它负责管理大内存区域中的内存分配,这些大内存区域就是通过一些页面粒度的内存分配函数(如VirualAlloc)来预定(reserve)的。 堆管理器中的分配粒度相对比较小:在32位系统上是8字节,在64位系统上是16字节。 堆管理器已经被 Windows 系统精心设计,在这些小内存分配的情况下会进行内存使用率和性能两个方面的优化。
一、内存为什么要对齐虽然所有的变量都是保存在特定地址的内存中,但最好还是按照内存对齐的要求来存储。这主要出于两个方面的原因考虑: 平台原因:不是所有的硬件平台(特别是嵌入式系统中使用的低端处理器)都能访问任意地址上的任意数据,某些硬件平台只能访问对齐的地址,否则会出现硬件异常。 性能原因:如果数据存放在未对齐的内存空间中,则处理器访问变量时需要进行两次内存访问才能完整读取该变量的值,而对齐的内存访问仅需一次访问。
一、为什么需要内存映射“内存映射文件”可以将硬盘上的文件映射到虚拟地址空间,这样就不需要将所有东西都放入到页交换文件中,比如系统有许多程序同时运行时,如果将这些程序文件都加载到页交换文件中,页交换文件将会变得非常大。事实上,Windows 也并没有将硬盘上的程序文件复制到页交换文件中,因为这样不仅会让页交换文件将会变得非常大,也会浪费很多时间,特别是可执行程序非常大的时候。 当用户要求执行一个应用程序时,系统会打开该应用程序的.exe文件,并计算出应用程序的代码和数据的大小,然后系统会在进程的虚拟地址空间预定一块地址空间,并注明与该区域相关联的物理存储器就是.exe文件本身。 当把一个位于硬盘上的文件(可以是.exe,.dll也可以是普通文件)映像用作地址空间区域对应的物理存储器时,我们称这个文件映像为“内存映射文件”。
一、页交换文件虚拟地址空间只是操作系统为进程“虚拟”出来的一块地址区域,并不代表任何实际的空间。而“页交换文件”却对应了实际的空间,这个空间一般是磁盘上名为“pagefile.sys”的文件。 “页交换文件”的大小和位置可以在系统设置(系统属性 -> 高级 -> 性能 -> 设置 -> 高级 )中进行设置: 从微软的官方文档来看,“虚拟内存”等于“物理内存”+“分页文件”总和。可以把“虚拟内存”理解为 Windows 的一种内存管理机制。
一、实模式下内存分配机制在 8086 或者 80186 以前,程序运行时,操作系统会把程序全都装入内存,程序都是直接运行在物理内存上的。也就是说程序中访问的内存地址都是实际的物理内存地址。当计算机同时运行多个程序时,必须保证这些程序用到的内存总量要小于计算机实际物理内存的大小。 例如某台计算机总的内存大小是128M ,现在同时运行两个程序 A 和 B ,A 需占用内存10M , B 需占用内存110M 。计算机在给程序分配内存时会采取这样的方法:先将内存中的前10M分配给程序 A ,接着再从内存中剩余的118M中划分出 110M分配给程序 B 。这种分配方法虽然可以保证程序 A 和程序 B 都能运行,但是这种简单的内存分配策略会导致很多问题: