几种垃圾收集收集的算法思想
标记清除算法
分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收被标记的对象。最基础的收集算法,缺点:一是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,可能导致在需要分配大对象时,无法找到足够连续内存而不不得不触发另一次垃圾收集动作。
复制算法
将内存分为相等的两块。每次只用一块,当这一块内存用完了,就将存活的对象复制到另一块上去,然后把已使用的内存空间一次性清理掉。这样使得每次都是对真个半区进行内存回收内存分配时也不用考虑内存碎片等复杂情况,只要移动堆顶指针按顺序分配内存即可。但是这个算法将内存缩小了一半,代价比较高。
一般采用这种收集算法来回收新生代,因为大部分对象都是“朝生夕死”的,不需要按1:1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor,当回收时,将Eden和Survivor中还存活的对象一次性复制到另一块Survivor空间,然后清理掉Eden和刚才使用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8:1,也就是每次新生代中可用内存空间为整个新生代容量的90%,只有10%的内存被“浪费”。当有回收时有多于10%的对象存活时,Survivor空间不够用了,就需要依赖其他内存(老年代)进行分配担保(Handle Promotion)
标记-整理算法
主要用于老年代的垃圾收集,标记过程仍然与“标记-清除”算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。
分代收集算法
当前商业虚拟机的垃圾收集都采用“分代收集”(Generational Collection)算法,这个算法并没有什么新思想,只是根据对象存活周期的不同将内存划分几块。一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中以为对象存活率高,没有额外空间对它进行分配担保,就必须使用“标记-清理”或者“标记-整理”算法来进行回收。