在现行的垃圾回收算法中主要有以下几种:
1.标记-清除算法
标记-清除算法是最基本的算法,后续的算法都是在这个基础上对其不足进行改进而得到的。
这个算法主要分为两步:1 标记所有活动的对象;2 清除已经死亡的对象(标记了的是活动的对象,未标记的就是可清除的对象)
第一步 :标记所有活动的对象 (另一种说法是标记所有需要回收的对象,无论是哪一种,其原理是一致的)
标记使用的是 深度优先遍历 (根 -左-右),原因是内存使用量要比 广度优先遍历 (层次遍历)要小。
具体实现:首先要标记根直接引用的对象,然后进行递归,伪代码就是:
mark(obj){
if(obj.mark == FALSE)//如果没有标记
obj.mark =TRUE //则设置为标记
for(child : children(obj))//递归处理子对象
mark(*child)
}
判断对象是否存活用的是前面介绍过的 对象的可达性分析
第二步:清除所有没有标记的对象
在这一步,我们遍历整个java堆,按顺序一个一个的遍历对象的标记标志位
1.设置了标志位的,表示是存活的对象,将标志位设为false,为下一次GC 的标记 做准备
2.没有设置标志位的,表示当前对象已经可以回收
该回收算法的优缺点
1.优点:算法简单,实现容易
2 缺点:一个是效率不高。二个是内存碎片化。在标记清除之后会产生大量的不连续的内存碎片,空间碎片太多会导致在需要分配较大的对象时,无法找到足够连续的内存而不得不提前触发下一次GC。
3.标记-清除 算法使用的时空闲链表
后面的算法均时根据该算法的缺点进行改进而衍生出来的
2.复制算法
原理:将可用内存分为大小相等的两块,每次使用其中一块,当使用的那一块内存用完后,将存活的对象复制到另一块上,然后一次性清理掉用完的那块空间,如此往复。
这样设计的目的时为了 解决效率问题,实现了传统意义上的空间换时间。
复制算法执行时分为三个重要步骤
1.标记存活对象
2.拷贝存活对象(已经标记的对象)到新的内存区域,并更新对象指针。
3.一次性清理已使用的内存空间
复制算法的优点
优点 :实现简单,运行高效,不会发生内存碎片化(移动指针,按序分配内存)
缺点:内存使用率低下(将内存一分为二,每一次只有一半的内存可用),存活对象较多时,效率同样不高
由于最原始的算法内存使用率太低,后来的商业虚拟机在这基础上有一定的改进。
在新生代中的对象98%的对象是“朝生夕死”,所以并不需要以1:1 的比例来分配 from 与 To 的空间,而是将内存分为一块较大的Eden空间跟两块较小的 Survivor空间(HotSpot中默认的时8:1:1),每次使用的是Eden与其中的一块Survivor空间。当回收时,将存活的对象复制到另一块Survivor空间上,最后清理掉Eden与survivor空间,这样能使用的内存空间就大大提高了。
如果剩下的Survivor空间不够存放存活的对象,就需要依赖其他内存来进行分配担保了,这些对象会直接通过分配担保机制直接进入老年代。
3.标记-整理算法 (又称标记-压缩算法)
标记-整理算法时在标记-清除算法上的一种改进。整理不会改变对象排列的顺序,只会缩小他们之间的间隙,把对象聚集到堆的一端。
标记阶段与前面的分析没有什么区别,整理压缩主要有以下三个步骤:
1.设定forwarding 指针
2.更新指针
3.移动对象
标记-整理算法的优缺点
优点:java堆的利用得到进一步的提高
缺点:整理是一个移动对象的过程,伴随着对象的移动以及指针的修改,这也是一笔不小的开销
4.分代收集算法
这是现在主流的商业虚拟机采用的垃圾回收算法,其主要思想是将Java堆分为 新生代 与 老年代。
对新生代采用一般采用复制算法(大量的对象死亡,回收效率比较高),对老年代就必须使用标记-清除或者标记-整理算法来进行回收
优缺点
优点:效率明显提升
缺点:新生代GC所花费的时间会增多,老年代GC会频繁运行。
现在常使用的垃圾收集器有如下几种
1.serial收集器 (新生代 单线程 复制算法)
jdk1.3.1之前是新生代收集的唯一选择
这是以一个单线程的收集器,它工作时会暂停其他所有的工作线程 也就是著名的 Stop- The -World
优点:由于是单线程,所以没有线程之间的切换开销
缺点:在其工作时会停止所有其他工作线程
2.ParNew 收集器 (serial的多线程版本) 新生代 多线程 复制算法
在单CPU的环境下,其效率还不如Serial收集器
3.Parallel Scanvenge 收集器 (新生代 多线程 复制算法)
这个收集器关注的不是系统停顿的时间,而是达到一个可以控制的吞吐量
4.Serial Old收集器 (老年代 单线程 标记-整理算法)
5.Paralle Old 收集器 (老年代 多线程 标记-整理算法)
关注点:吞吐量
6.CMS收集器 Concurrent Mark Sweep(老年代 多线程 标记-清除算法)
关注点:获取最短回收停顿时间为目标的收集器
CMS收集器工作流程分为四个步骤
1.初始标记//stop the world 标记以下GC Roots能关联到的对象
2.并发标记// 标记GC Roots之后的对象
3.重新标记//stop the world 修正并发期间用户程序继续运行产生变动的那一部分对象的标记记录
4.并发清除
这是一款真正意义上的 并发 垃圾收集器
缺点
1.对CPU资源非常敏感
2.无法处理浮动垃圾(垃圾收集的过程中,应用程序运行产生的其他垃圾)
3.因为时基于标记-清除算法,所以依旧会产生大量的内存碎片
7.科技的最前沿 G1收集器
G1的优点有以下几点:
1.并行与并发
2.分代收集
3.基于标记-整理算法,会对空间进行进一步的整合
4.Stop-The-World 的时间可以预测