Serial 收集器
Serial垃圾收集器是单线程垃圾收集器,在执行GC的时候会暂停用户线程(STW),专心执行垃圾回收操作,其年轻代回收采用的标记复制算法,老年的回收采用的标记整理算法。Serial Old收集器是Serial收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:一种用途是在JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用,另一种用途是作为CMS收集器的后备方
Parallel Scavenge收集器
Parallel 是Serial收集器的多线程版本,除回收垃圾采用多线程外,其他方式与Serial 收集器类似。其默认采用的回收线程数与cpu 核心数相等。该值一般不需要修改。其关注点在于吞吐量(高效使用CPU),吞吐量时指用户线程使用CPU的时间占整个CPU使用时间的比值。新生代采用标记复制算法,老年代采用标记整理算法。Parallel Old收集器是Parallel Scavenge收集器的老年代版本。使用多线程和“标记-整理”算法。在注重吞吐量以及CPU资源的场合,都可以优先考虑 Parallel Scavenge收集器和Parallel Old收集器(JDK8默认的新生代和老年代收集器)。
ParNew收集器
ParNew收集器和parallel收集器类似,其区别在于ParNew收集器可以和CMS收集器配置使用。新生代采用标记复制算法,老年代采用标记整理算法。它是许多运行在Server模式下的虚拟机的首要选择,除了Serial收集器外,只有它能与CMS收集器(真正意义上的并发收 集器,后面会介绍到)配合工作。
CMS收集器
CMS(Concurrent Mark Sweep)收集器是一种获取最短用户线程停顿时间为目标的收集器。<br>它是hotStop 虚拟机第一个真正意义上的并发收集器。它第一次实现了用户线程和垃圾回收线程同步工作(基本)。<br>CMS 是一个基于标记清楚算法的垃圾收集器,设置jvm 参数开启XX:+UseCMSCompactAtFullCollection,<br>实现标记整理以减少标记清除带来的大量内存碎片问题。<br>
CMS垃圾收集器回收过程
初始标记
该过程在执行GcRoot时,会先标记GcRoot的直接引用。该过程也会暂停用户线程,但它的标记速度相当快。
并发标记
该过程不会停止用户线程,而是标记线程和用户线程同时执行。并发标记阶段就是从GCROOT直接关联的对象开始遍历整个对象图。这个过程耗时长,但是由于其是与用户线程并发执行,所以用户很难感知到该过程。在整个并发标记的过程中也正因为用户线程还在执行会导致被标记的对象发生改变的问题。
重新标记
重新标记是解决并发标记过程中产生的对象状态发生改变问题。该过程需要暂停用户线程,时间比初始标记长,但远比并发标记时间短。其采用三色标记法和增量更新算法
并发清理
开启用户线程,同时GC线程对未标记的对象进行统一清理,这个阶段由用户线程产生的对象直接标记为黑色对象,不做任何处理。
CMS垃圾收集器缺点
CMS会跟用户线程抢占CPU资源导致吞吐量下降。
CMS在做并发标记和并发清理的过程中不会暂停用户线程,这就导致会出现浮动垃圾(标记了的垃圾对象),只能等到下次GC才能清理。
CMS采用标记清除算法实现的垃圾对象清理,就会出现大量不连续的内存碎片。可以采用设置jvm 参数让其在做完GC后进行标记整理
执行过程中的不确定性,会存在上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况。例如在执行CMS回收过程中,触发了年轻代GC 而 s0 s1区域存不下,直接放到老年代区域,恰好老年代也放不下就再次触发GC。此时CMS 回收就会触发concurrentmode failure,导致直接暂停用户线程使用serial old 垃圾收集器来回收。
CMS并发标记问题
三色标记法 :GCROOT 可达性分析过程中按照是否访问过对象,<br> 给予对象标记三种不同的颜色。<br>
黑色 : GCROOT 完全访问过该对象和其内部引用的全部对象
灰色 : GCROOT 访问过该对象,但其引用的对象未被完全访问
白色:GCROOT 未访问过的对象
什么是漏标?
并发标记对象过程中,由于用户线程未停止,可能出现黑色对象引用白色对象,而黑色对象已经被GC ROOT 访问过,白色对象不会在被标记,导致CMS会将其作为垃圾对象回收
多标—浮动垃圾
在并发标记过程中,如果由于方法运行结束导致部分局部变量(gcroot)被销毁,这个gcroot引用的对象之前又被扫描过 (被标记为非垃圾对象),那么本轮GC不会回收这部分内存。这部分本应该回收但是没有回收到的内存,被称之为“浮动 垃圾”。浮动垃圾并不会影响垃圾回收的正确性,只是需要等到下一轮垃圾回收中才被清除。另外,针对并发标记(还有并发清理)开始后产生的新对象,通常的做法是直接全部当成黑色,本轮不会进行清除。这部分 对象期间可能也会变为垃圾,这也算是浮动垃圾的一部分。
CMS解决漏标问题
三色标记法,见上文
增量更新
当黑色对象加入新的白色对象引用时,将这个新的引用记录下来,在进行重新标记时,再已记录的黑色对象为根进行重新标记。也就是黑色对象加入新的引用时将黑色对象变为灰色对象
跨代引用问题—记忆集&卡表
垃圾回收器在回收垃圾的过程中,有可能会出现老年代引用年轻代的情况。而在做minorGC 时 就会出现找不到GCRoot的情况 ,导致垃圾回收出错。为了解决这种情况jvm 使用了 记忆集与卡表来解决该问题。
记忆集是一种数据结构卡表示记忆集的具体实现,老年代会分成512个字节大小的卡页,年轻代会维护一张卡表数组,其中用0和1 记录老年代卡页中是否有引用年轻代对象,一个卡页中可包含多个对象,只要有一个对象的字段存在跨代指针,其对应的卡表的元素标识就变成1,表示该元素变 脏,否则为0。年轻代在做GCroot时会将这些脏引用加入到年轻代GCRoot 进行扫描。
G1垃圾收集器
G1(Garbage-First)是一款面向服务器的垃圾回收器,主要用在多CPU和大内存服务的垃圾回收,以极高的效率满足垃圾回收暂停用户线程时间,同时满足高吞吐量
G1内存模型
G1 将JAVA 堆内存划分为很多个region区域,该区域最多有2048个。一般单个region区域大小为整个JAVA堆大小除以2048。例如 现在JAVA 堆大小为4G,那么 "region大小 = 4096M % 2048 = 2 M"。当然这个region大小也可以通过G1 jvm 参数-XX:G1HeapRegionSize进行制定,但是一般采用默认值。
G1在逻辑上保留了年轻代老年代的概念。但在实际堆内存中它们并不是连续的而是分成很多个region区域。
最开始年轻代默认是占整个堆内存的%5,例如整个堆的大小是4096M,那么年轻代大概占有200M空间。这个比例也可以通过jvm参数-XX:G1NewSizePercent进行修改,一般选择默认。G1收集器在会根据实际服务的运行情况动态的增大年轻代的空间大小。这个增加也是有限度的,默认是整个堆空间大小的60%,这个值也是可以通过jvm参数-XX:G1MaxNewSizePercent进行调整。
同样年轻代里Eden区域和s0、s1 区的大小默认也是8:1:1 也就是160m、20m、20m。还有一点,一个年轻代的region区域被回收后,可能之后这个区域被分配给了老年代。所有region区域的分代不是固定的,它在之后的服务运行期间有可能是G1 得任何一个分代阶段
G1 收集器和之前的垃圾收集器将对象转移到老年代的机制一样,有一点不同的是G1收集器将大对象转移到humongous区,而不是直接放到老年代的region区域。G1 对大对象的判断规则是一个对象超过region区域的50%,就会放入humongous区域。如果该对象太大可能横跨多个region区域存放。将大对象直接放入humongous区域可以节约老年代的区域,减少因为老年代空间不足带来的GC 性能开销。
G1垃圾回收过程
初始标记
暂停用户线程,GCRoot 只是扫描直接引用,不会进行深度扫描,该过程非常快。
并发标记
同CMS,用户线程与垃圾回收线程并行,GCroot对所有引用对象进行深度扫描。
最终标记
同CMS的重新标记,但这里使用的是原始快照的方式进行漏标问题解决。
筛选回收
该过程不同与CMS的并发清理,这个过程会暂停用户线程。根据设置的jvm参数-XX:MaxGCPauseMillis GC停顿时间(默认200ms),对需要回收的region区域进行筛选。优先回收那些回收价值较大的区域
什么是回收价值
例如region区域A存活对象有50个,区域B 存活对象只有1个,那么复制一个所用的时间当然比复制50个对象所用的时间要少。那么我们就说区域B的回收价值大于区域A的回收价值
筛选回收说明
G1的回收算法是复制算法,就说将一个存活对象复制到另一个区域,然后将之前的区域进行回收。这个过程不同与CMS的标记清楚算法,其产生的内存碎片就会很少。G1收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的Region(这也就是它的名字Garbage-First的由来),比如一个Region花200ms能回收10M垃圾,另外一个Region花50ms能回收20M垃圾,在回收时间有限情况下,G1当然会优先选择后面这个Region回收。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限时间内可以尽可能高的收集效率
G1 收集器的优点
并发与并行:在多个cpu环境下,G1充分利用多核条件,来减少用户线程的停顿时间。相比于其他一些需要暂停用户线程来执行垃圾的回收器,G1可以实现垃圾回收过程和用户线程并发执行。
分代收集:G1不需要其他收集器的配合,但它还是保留了分代收集概念。
G1 最底层采用的是复制算法,很少会产生内存碎片。
可预测的gc停顿:这是G1 相对于CMS的一个最大优点,G1 在做GC的过程中可以通过设置jvm参数-XX:MaxGCPauseMillis 来指定gc停顿时间。可以让用户根据时间服务运行情况设置该值,让GC停顿时间和吞吐量达到平衡。
G1垃圾收集分类
young GC : Eden区放满之后并不是马上触发,而是计算一次GC回收年轻代区域的时间与jvm通过 -XX:MaxGCPauseMills设置的回收时间进行比较,如果这个值远小于我们设置的值,那么G1 会增大年轻代区域大小,直到计算出GC回收时间接近我们设置的时间时才进行young GC。
Mixed GC : 不是full GC ,当老年代达到jvm 设置的 最大占比时,触发mixed GC ,这个过程会回收全部年轻代和部分老年代(会根据用户设置的回收时间判断对象回收优先级,并不是每次都是全部回收老年代对象)以及大对象区域(Humongous)。回收过程中采用复制算法将非垃圾对象拷贝到另一个未使用过的内存区域。如果未使用的区域内存不足,会触发一次fullGC 。
Full GC : 暂停用户线程,采用单线程标记整理回收垃圾对象。空闲出region区域提供下次mixedGC复制对象。该过程会非常的慢、耗时。
G1回收器使用场景
50%以上的堆被存活对象占用
对象分配和晋升的速度变化非常大
垃圾回收时间特别长,超过1秒
8GB以上的堆内存(建议值)
停顿时间是500ms以内