|GlobalLock的前世今生1:古老年代

|GlobalLock的前世今生1:古老年代

在很久很久以前 , 那个时候的操作系统还是Windows 1.0 。 当时确实是一段十分古老的年代 , 你可以看到那个时代的各种标志:段 , 近指针和远指针 , 没有虚拟内存 , 协作式多任务 。
因为操作系统中没有虚拟内存 , 如果操作系统向要执行交换(Swapping) , 操作系统必须和应用程序一起协作来完成 。 如果需要分配内存但是没有一段连续的内存块可供分配 , 则内存管理器只能执行一项称之为”压缩”(Compaction)的操作 , 来确保一段连续的内存可供分配 。
> 代码段可以被完全地释放 , 因为它们可以从原始执行程序中重新加载(因为没有虚拟内存 , 就没有所谓的页换出) 。将代码释放需要一些额外的工作来确保下次代码被调用的时候 , 它已经从内存中重新加载过了 。 如何确保这一点和今天文章的内容不太相关 , 但是它确实是一项非常复杂的过程 。
> 包含有代码的内存块可以来回移动 , 而且对于旧地址的引用可以被修正以指向新的地址 。 这个过程也是十分复杂 , 在此就不在展开了 。
> 包含有数据的内存块可以来回移动 , 但是对旧地址的引用无法进行修正 。 应用程序有责任来在内存移动的时候确保指针指向正确的位置 。
> 被锁定或者固定式内存块不会被移动 。
当你使用GlobalAlloc的时候 , 你需要偶先做出一个决定 , 就是:是否希望分配的内存是可移动的(此内存可以由内存管理器来回移动)还是固定式的(内存不受移动的影响) 。
从概念上来说 , 一块固定式内存可以看做是一段永久性锁定的可移动内存块 。
在那个年代 , 应用程序不被鼓励分配固定式内存 , 因为它会影响内存管理器的操作 。(试想 , 碎片整理程序会面对这种不可移动的内存块而无法工作 。 )
【|GlobalLock的前世今生1:古老年代】GlobalAlloc的返回值是全局内存块的句柄或HGLOBAL类型 。这个值本身是没有用的 。你必须调用 GlobalLock() 才能将此 HGLOBAL 转换为您可以使用的指针 。
GlobalLock会执行如下的操作:
> 它强制内存提交(如果它之前被丢弃) 。可能需要丢弃或移动其他内存块 , 从而为被锁定的内存块腾出空间 。
> 如果内存块是”可移动的” , 那么它还会增加内存块上的”锁计数” , 从而防止内存管理器在压缩期间移动内存块 。(”固定”内存上的锁定计数不是必需的 , 因为它们无论如何都无法移动 。 )
鼓励应用程序仅在必要时锁定全局内存块 , 以避免将堆碎片化 。禁止指向未锁定的可移动内存的指针 , 因为即使是最微小的操作——比如调用一个碰巧被丢弃的函数——都会导致压缩并使指针无效 。
那么 , 这一切又和GlobalReAlloc有什么关联呢?
它取决于内存是如何分配的 , 它的锁状态是什么 。 它取决于内存是如何分配的 , 它的锁状态是什么 。
如果内存被分配为“可移动”并且它没有被锁定 , 那么内存管理器被允许为系统中其他地方的内存找到一个新的家并更新它的簿记 , 以便下次有人调用 GlobalLock() 时 , 他们得到 指向新位置的指针 。
如果内存被分配为“可移动”但它被锁定 , 或者如果内存被分配为“固定” , 那么内存管理器只能就地调整它的大小 。它无法移动内存 , 因为(如果可移动和锁定)仍然有未完成的指针指向它 , 如非零锁定计数所证明的那样 , 或者(如果固定)在假设它永远不会移动的情况下分配了固定内存 。

相关经验推荐