书上这样说,由于垃圾收集算法的实现涉及大量的程序细节, 而且各个平台的虚拟机操作内存的方法区又不相同,因此可不过多的讨论算法的实现,只需了解几种算法的思想及其发展过程。
有以下几种算法:
标记-清除算法,复制算法,标记-整理算法,分代收集算法
标记-清除算法:是其他几种算法的基础 ,就是把要回收的内存做个标记,然后根据标记去回收内存。
这种算法的不足之处有两点:一,效率不高,需要先去进行标记,然后再根据标记清除;二,这种算法会导致过多的不连续内存的诞生,这样内存碎片太多,当程序运行过程中需要分配 较大对象时,无法找到足够的连续内存 而不得不触发又一次垃圾收集工作。
复制算法:复制算法主要是针对标记-清除算法效率的问题而提出的一种方案:即把内存按容量分为等大的两块,每次只使用其中的一块,当这一块的内存用完了,就把还存活着的对象复制到另一块上面,然后再把使用过的这一块一次清理掉。
现在的很多商业虚拟机都采用这种收集算法来回收新生代,IBM公司研究表明,新生代中的对象98%都是朝生夕死,所以并不需要按照对半来分内存,内存分为较大的一块Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor空间,回收时,把Eden和这块Survivor空间上还存活着的复制到另一块Survivor空间中,然后再一次性清理掉(HotSpot虚拟机默认Eden和Survivor的比例是8:1),但是在没有办法保证每次存活的对象都不多于10%时,我们需要有其他内存(老生代)进行分配担保,也就是其中一块Survivor空间不足以装下Eden和Survivor空间存活下来的对象时,我们会借用老生代中的内存。
标记-整理算法:根据老生代的特点,有人提出一种“标记-整理”算法,标记过程仍然和标记-清除算法一样,只是后续回收的时候不同,标记-整理是不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存,然后这样就能得到连续的内存,而不是像标记-清除一样,得到的通常都是内存碎片。
分代收集算法:目前所有的商业虚拟机都采用“分代收集算法”,但其实这并不是什么新思想,只是根据对象存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老生代,这样根据各个年代的特点采用最适当的收集算法。
比如:在新生代中,每次垃圾收集时都有大批对象死去,只有少量存活,那就选用复制算法;
在老生代中,因为对象存活率高,没有额外空间对他进行分配担保,故采用标记-清除算法或者标记-整理算法。