接上一篇我们谈谈来垃圾回收算法

三、垃圾回收算法

1. 标记-清除算法

标记清除算法是最为基础的垃圾回收算法,顾名思义就是将对象结点如果被标记上为无用的(上篇我们谈到了如何标记一个对象为无用的),且在标记完成后统一回收掉所有被标记的对象。之所以称同时最基础的算法就是因为之后的算法都是根据他的缺点进行的改进。

缺点

  • 效率问题,标记和清除过程的效率都不高。
  • 空间问题,标记清除会产生大量的不连续的内存碎片,对于大对象这些碎片是不可用的。

学过操作系统的可以看出这些垃圾回收算法类似于操作系统的内存回收和分配算法。

标记清除算法示意图

2. 复制算法

复制算法是为了解决标记-清除算法中的效率不高的问题,他的实现是将内存均分为两块,每次只是用其中的一块,如果一块用完了就将还存活的对象复制到了另一块里面,然后把自己的空间一次性清理干净。

优点:

  • 实现简单
  • 运行高效,不用考虑内存碎片

缺点:

  • 内存只能使用一半

现代商业虚拟机一般都采用这种算法来回收新生代的内存,新生代的对象98%都是朝生夕死的,所以可以不按照对半来划分内存,而是有一块比较大的Eden区和两个比较小的Survivor中还存活着的对象一次性拷贝到另一块Survivor空间上,最后清理掉Eden和刚才的Survivor区。HotSpot虚拟机中这个配置是Eden:survivor = 8:1。

这里还有一点要特别提到那就是,我们无法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用的时候就需要依赖其他内存(比如老年代)进行分配担保。

复制算法示意图

3. 标记-整理算法

标记-整理算法顾名思义就是对于标记-清除后的残留的对象进行一次整理,让所有的对象都向一端移动,达到让出后面一片连续空间的目的。

标记-整理算法示意图

4.分代收集算法

就是根据不同年代的各自特点选用不同的回收算法。

5. 垃圾回收器

垃圾回收算法可以认为是方法论,而垃圾收集器是垃圾回收算法的具体实现。

  • Serial收集器

Serial是串行的意思,就像他的名字一样它是一个单线程的收集器,他只会用一个CPU或一条线程去完成回收垃圾的任务。所以他在回收垃圾的时候必须暂停所有的工作进程(Sun将它称为Stop The World),直到他收集结束。

Serial收集器示意图

他的缺点就是停顿时间过长,也就是Stop the World的时间过长,而优点就是简单而高效(与其他单线程收集器相比),对于单个CPU来说,Serial收集器没有线程交互的开销,专心做垃圾收集自然可以获得较高的单线程效率。

​ 是Client端首选的新生代垃圾收集器

  • ParNew收集器

这个收集器其实就是Serial的多线程版本,除了多条线程进行垃圾收集外其余都和Serial都一样,实现这两种收集器共用了许多代码。

是Server模式下的虚拟机首选的新生代收集器

  • Parallel Scavenge收集器

这也是一款新生代收集器,他也是使用了复制算法,并且是并行的多线程收集器,不同的是他可以达到一个可控制的吞吐量,也就是控制GC的时间在全部运行时间里面的占比。

  • Serial Old收集器

是Serial的老年代版本。也是一个单线程收集器,使用了"标记-整理"算法。

  • Parallel Old收集器

Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法。注重吞吐量和CPU敏感的场合可以优先考虑Parallel Scavenge加Parallel Old收集器。

  • CMS收集器

CMS全称Concurrent Mark Sweep 收集器是一种以获取最短回收停顿时间为目标的收集器。从名字上我们就可以猜到他采用了标记清除算法,它的运行过程比前面的收集器要复杂一点,分为四个步骤:

  • 初始标记

    需要Stop The World,仅仅标记一下GCRoots能直接关联的对象,速度很快

  • 并发标记

    就是在GC的Roots Tracing的过程

  • 重新标记

    需要STW,为了修正并发标记期间,用户继续运作而导致标记产生变动的一部分对象标记记录。

  • 并发清除

缺点:

    • CMS收集器对CPU资源非常敏感
    • CMS收集器无法处理浮动垃圾
    • 收集结束会有大量碎片空间无法利用
    • G1收集器

    G1收集器是基于标记-整理而实现的收集器,也就是它不会产生内存碎片,二是他可以非常精确的。G1将整个Java堆(包括新生代和老年代)划分为多个固定的独立区域。

    最后修改:2022 年 02 月 23 日
    如果觉得我的文章对你有用,请随意赞赏