JVM垃圾回收器
JVM垃圾回收算法只是一种思想,一种方法论,而垃圾回收器是垃圾回收的具体实现。
1、Serival收集器
串行收集器,是最古老,最稳定的垃圾收集器,效率也比较高。
- 单线程收集
- 需要暂停用户线程
- 新生代复制算法,老年代标记整理算法
- 参数控制:-XX:+UseSerival串行收集器
由于是单线程的原因,回收速度比较慢,可管理的堆内存空间比较小。
2、ParNew收集器
多线程版本的Serival收集器
- 吞吐量比较高:并发收集,利用多核CPU的优势,大幅度降低gc时间。
- 需要暂停用户线程
- 新生代采用复制算法。,老年代标记整理算法
- 参数控制 -XX:+UseParallelGC-XX
- 适用于需要有效利用CPU资源,容忍暂停的场景
上述的两种收集器在进行垃圾回收的过程中,都需要stop the world【暂停用户线程】,在如今的B / S系统的服务器上,长时间的不响应用户是无法忍受的。因此逐渐衍生出了不需要长时间stw的垃圾收集器
3、CMS 垃圾收集器
CMS : Concurrent Mark Swap 是一种追求最短停顿时间的垃圾收集器。基于标记–清除算法,针对老年代。
3.1 回收步骤
主要分为标记和清除两个步骤。
为了减缓Stop the word的问题,使得最大程度上垃圾回收线程和用户线程并行,将整个标记清除分为以下几个阶段。
- 初始标记:需要stop the world ,仅仅标记与GC Roots直接相连的对象,因为这部分对象毕竟是少数以及一些如OopMap的优化技术,因此用户线程暂停时间较短。
- 并发标记:遍历所有与GC Roots间接相关联的对象也就是遍历整个对象构成的图,这部分对象比较多因此时间比较长,但是标记线程可以和用户线程并发执行,不需要Stop the world。但是这必然存在一个问题,在用户线程执行的过程中又会产生一些垃圾,这部分可能会标记不到。正如你妈妈在打扫房间的时候,你依旧在仍纸团。
- 重新标记:重新标记一次,标记在并发标记过程中,用户线程产生的垃圾对象,这一步需要Stop the world。因为在并发标记过程中产生的垃圾相对较少,因此这一步stop the world的时间也比较短。
- 并发清除:清理掉之前所标记的对象,因为需要清理的对象比较多,这一步比较耗时,因此将用户线程和垃圾回收线程并发执行,不需要stop the world。
3.2 存在的问题
- CPU敏感:CMS默认使用(CPU数量 + 3) / 4个线程来进行垃圾回收,因此当服务器的CPU个数小于4个话,对于CPU来说是一个较大的负担。但如今服务器的CPU数量基本都大于四个。
- 浮动垃圾问题:因为在并发清理的过程中没有stw,用户线程还是会产生一部分垃圾,这部分垃圾也需要占用一一定的内存空间,因此需要进行空间预留,因此必须提前进行垃圾回收,不能等老年代几乎填满了才进行垃圾回收。JDK6之后大概老年代空间使用了92%左右就会触发垃圾回收。但是还存在一个问题,万一有什么大对象的垃圾产生,预留的空间放不下,此时就需要切换垃圾回收器为SerivalOld这么一个单线程且完全STW的垃圾收集器进行垃圾回收。
- 内存碎片问题:因为基于标记—清除算法,本来就有这个问题,但为什么要使用这个算法呢?因为CMS绝大多数情况都和用户线程并行,因此如果使用整理或者复制算法会动态的改变对象的位置,为了解决这个问题,在需要分配大对象的时候,使用一次整理算法。
上面的几种垃圾回收器都关注的是如何快速的整理整个堆内存,但是仔细想一下,只要垃圾回收的速率高于对象分配的速率,是完全不影响程序的正常运行的,因此不需要对整个堆内存进行回收,只要使得回收产生的空闲空间大于程序需要的空间即可。
4、G1垃圾收集器
G1 : Gabage First 开启参数-XX:+UseG1GC。该垃圾回收器将整个堆内存化整为零,划分为一个小小的region,region大小的范围为1–32MB具体根据参数-XX:G1HeapRegionSize来进行设置,且应为2的次幂。G1仍然使用分代的思想,每一个region可以划分为Eden,survival(From,To)区,为了存储大对象又引入了一个Humongous这么一个区域。其中这些区域可以是不连续的,如下图所示:
G1的垃圾回收过程和CMS的类似,也分为初始标记,并发标记,重新标记 最后一步筛选回收,不过最后一步是需要stw的,因为G1仅仅回收效益最大的几个region不需要对整个堆内存进行回收,再加上多线程回收所以消耗的时间很短,不需要stw。
4.1三色标记法
之前的标记过程是stw的,因此在标记过程中,对象与对象的引用不会再发生改变,但是由于CMS和G1都在这一步都不需要STW,因此必须解决并发标记过程中对象的引用发生变化问题,为此引入了三色标记法。
每个对象在不同的扫描标记状态对应不同的颜色:
- 黑色:该对象所有的子对象都已经被扫描过,或者自身是根对象。
- 灰色:自身已经被扫描过,但还存在子对象没有被扫描。
- 白色:自身还没有被扫描,如果垃圾回收器扫描完,还有对象的颜色是白色的,说明就是垃圾。
那么三色标记法如何解决并发标记过程中对象的引用发生改变的呢?
- 增量更新:CMS使用的算法,当引用增加后,再进行回收的时候,会重新扫描一次。
- 原始快照:回收前后两次的对象快照进行对比,就可以发现,是否有新引用的产生。
。。。。待续