虚拟存储系统是指什么 虚拟存储( 五 )


下次适配:每次从上次查询结束的地方开始搜索,直到遇到合适的空空闲块 。这种策略通常比第一种适应更有效,但是存储器利用率低得多 。
最佳适应:检查每个空空闲块,并选择适合所需请求大小的最小空空闲块 。最佳匹配的内存利用率是三种策略中最高的,但是它需要对堆进行彻底的搜索 。
搜索链表的效率是线性的 。为了减少分配请求匹配空空闲块的时间,分配器通常采用隔离存储的策略,即维护多个空空闲链表,其中每个链表的块大小大致相等 。
一个简单的分离存储策略:分配器维护一个空空闲链表数组,然后把所有可能的块划分成一些等价的类(也叫大小类),每个大小类代表一个空空闲链表,每个大小类的空空闲链表包含大小相等的块,每个块的大小在这个大小类中 。
当有分配请求时,我们检查相应的空空闲链表 。如果链表不是空,那么分配第一个块的全部 。如果链表是空,分配器向操作系统请求一个固定大小的额外内存片,将这个片分成大小相等的块,然后将这些块链接起来,形成一个新的空空闲链表 。
要释放一个块,分配器只需将该块插入相应的空空闲链表的头部 。
垃圾回收编写C程序时,一般只能显式分配和释放堆中的内存(malloc()和free()) 。程序员不仅需要分配内存,还需要负责内存的释放 。
很多现代编程语言都内置了自动内存管理机制(C/C++也可以通过引入自动内存管理库来实现自动内存管理) 。所谓自动内存管理,就是自动判断不再需要的堆内存(称为垃圾内存),然后自动释放垃圾内存 。
自动内存管理的实现是垃圾收集器,垃圾收集器是一个动态内存分配器,它会自动释放应用程序不再需要的已分配块 。
垃圾收集器通常采用以下两种策略之一来确定堆内存是否为垃圾内存:
引用计数器:在数据的物理空空间增加一个计数器 。当有其他数据与之相关(引用)时,计数器加1,否则减1 。通过定期检查计数器的值,只要是0,就认为是垃圾内存,可以释放它占用的分配块 。使用基准计数器,实现简单直接,但缺点也很明显 。它不能回收被循环引用的两个对象(假设有对象A和对象B,相互引用,但实际上对象A和对象B都是无用对象) 。
可达性分析:垃圾收集器将堆内存看作一个有向图,然后选择一组根节点(比如在Java中,一般是类加载器、全局变量、运行时常量池中的引用类型变量等 。),并且根节点必须是足够“活跃”的对象 。然后计算根节点集的可达路径 。只要从根节点不可达的节点都被视为垃圾内存 。
有如下几种垃圾收集器回收算法:
标记-清除:算法分为两个阶段:标记和扫描 。首先标记所有需要回收的对象,然后在标记完成后统一回收所有被标记的对象 。Mark-clear算法实现简单,但是效率不高,而且会产生大量的内存碎片 。
标记-排序:标记-排序算法与标记-清除算法基本相同,只是下一步不是直接清理可回收对象,而是将所有活对象移到一端,然后直接清理边界外的内存 。
复制:将程序拥有的内存空分成两个大小相等的块,每次只使用其中一个 。当这块内存用完时,将幸存的对象复制到另一块内存中,然后清理已用的内存空 。这种方法不需要考虑内存碎片的问题,但是内存利用率很低 。这个比例不是绝对的 。比如为了避免浪费,HotSpot虚拟机将内存分为Eden空和两个幸存者空,每次只使用Eden和一个幸存者 。回收时,将伊甸园和幸存者中幸存的对象一次性复制到另一个幸存者空房间,然后将刚刚使用的伊甸园和幸存者空之间的房间清理干净 。热点虚拟机中Eden和Survivor的默认比例是8: 1,只有10%的内存空会被浪费 。
生成:生成算法根据对象生命周期的不同,将内存划分为多个块,以便针对不同的年龄采用不同的回收算法 。一般分为新生代和旧时代 。新生代存储存活率低的对象,可以使用复制算法 。老年储存存活率高的物件 。如果使用复制算法,内存空空间将不够用,因此必须使用标记清除或标记排序算法 。
摘要虚拟内存是内存的抽象 。支持虚拟内存的CPU需要通过虚拟寻址来引用内存中的数据 。CPU加载一个虚拟地址,然后将其发送到MMU进行地址转换 。地址翻译需要硬件和操作系统的密切配合,MMU通过页表的方式获取物理地址 。
首先,MMU将虚拟地址发送到TLB以获得PTE(根据VPN寻址) 。
如果PTE恰好缓存在TLB,它将被返回给MMU,否则MMU需要从缓存/内存中获取PTE,然后将缓存更新到TLB 。

推荐阅读