垃圾回收算法
由于垃圾回收算法的实现涉及大量的程序细节,而且各个平台的虚拟机操作内存的方法又各不相同,因此本届不打算过多的讨论算法的实现,只是介绍几种算法的思想及发展过程。
标记-清除
标记-清除(Mark-sweep)算法,分为“”标记”和“清除”两个阶段
首先标记所有需要回收的对象,比如:有一个计数器,对象如果有地方引计算器就加1,当引用失效计算器就减去1,标记为0对象表示不可能再被使用
当触发gc的时候回收所有为0 的对象,
但是它主要不足有两个:一个是效率问题,标记和清除效率都不高;另一个是空间问题
因为标记清除后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后的在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次的垃圾收集动作。
复制回收算法
为了解决效率问题,一种为“”复制“”(copying),它是将可用的内存安容量分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活的对象复制到另一块上(上一节讲述的from 和to )to,然后再把已经使用过得内存空间一次清掉。这样使得每次都是对整个半区进行内存回收,内存分配时也不用考虑碎片等复杂情况,只要移动堆顶指针,
按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为了原来的一半。复制算法的执行过程如图:
新生代将内存分为较大的Eden空间和两个survivor空间,每次使用Eden和其中一块Survivor.当回收时,将Eden和Survivor 中还存活的对象一次性的复制到另一块Survivor。最后清理掉Eden和刚才用过的Survior空间。HotSpot虚拟机默认Eden和survivor的大小比例是8:1:1
也就是说新生代中可用内存空间为整个新生代容量的90%(80%+10%),只有10%的内存会被“”浪费”。我们没有办法保证每次回收都只有不多于10%的存活对象,对Survior空间不够用时
需要依赖其它内存(这里指的是老年代)进行分配担保(handle Promotion).
标记-整理
复制回收算法在对象存活较高时就要进行较多的复制操作,效率将会变低。更关键的是如果不想浪费50%的空间,就需要额外的空间进行分配担保,以应对被使用的内存中所有的对象都是100%存活的极端情况,所以在老年代一般不能直接选用这种算法。
根据老年代的特点,有人提出了另外一种“”标记-整理“(Mark-Compact)算法标记过程与“”标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存,“标记-整理”如图
参考深入理解Java虚拟机 JVM高级特性与最佳实践