JAVA垃圾回收器

JAVA垃圾回收器_第1张图片

Serial 收集器

JAVA垃圾回收器_第2张图片

       这是一个新生代的单线程收集器,它会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它运行垃圾回收时,必须暂停其他所有的工作线程(Stop The World),直到它收集结束。它依然是虚拟机运行在Client模式下的默认新生代收集器。它的优点是简单而高效(与其他收集器的单线程相比)。采用算法为复制算法

ParNew收集器

JAVA垃圾回收器_第3张图片

       ParNew收集器其实就是Serial收集器的多线程版本,它是HotSpot虚拟机中第一个真正意义上的并发收集器,他第一次实现了让垃圾收集线程与用户现场(基本上)同时工作。采用复制算法实现。

Parallel Scavenge收集器

       它是一个新生代收集器,他也是使用复制算法的收集器也是并行的多线程收集器,他的特点是它的关注点与其他收集器不同,它的目标是尽可能控制吞吐量。吞吐量 = 运行用户代码时间/(运行用户代码时间 + 垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉了1分钟,那吞吐量就是99%。比较适用于在后台运算而不需要太多交互的任务

Serial Old收集器

JAVA垃圾回收器_第4张图片

       它是Serial收集器的老年代版本,同样是一个单线程收集器,使用”标记-整理“算法。只要意义是在于给Client模式下的虚拟机使用。

Parallel Old

JAVA垃圾回收器_第5张图片

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

CMS(Concurrent Mark Sweep)收集器

JAVA垃圾回收器_第6张图片

       CMS收集器是一种以获取最短回收停顿时间为目的的收集器,适用于重视响应速度,希望系统停顿时间最短等场合

它基于”标记-清除“算法,,整体分为4个步骤:

1. 初始标记:标记一下GC Roots能直接关联到的对象

2. 并发标记:进行GC Roots Tracing的过程

3. 重新标记:修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录

4. 并发清除

       其中,初始标记、重新标记仍然需要”Stop The World“。初始标记只是标记一下GC Roots能直接关联到的对象,速度很快。并发标记就是进行GC Roots Tracing的过程。重新标记是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短。

       其中耗时最长的并发标记和并发清除过程都可以与用户线程一起工作。所以从整体上看,CMS收集器的内存回收是与用户线程一起并发执行的。

优点:并发收集、低停顿

缺点:

1. CMS收集器对CPU资源敏感,在并发阶段,他虽然不会导致用户线程停顿,但是会因为占用一部分线程(或者说CPU资源)而导致应用程序变慢,总吞吐量降低。

2. CMS收集器无法处理浮动垃圾,可能出现”Concurrent Mode Failure“失败而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当前收集中处理掉它们,只好留在下一次GC 时再清理掉。这也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎被完全填满了再进行收集,而是需要预留一部分内存空间提供并发收集时的程序运作使用。

3. 它是基于”标记-清除“算法的,会产生大量的空间碎片

G1收集器

JAVA垃圾回收器_第7张图片

G1(Garbage-First)收集器具有以下特点

1. 并行与并发: G1能充分利用CPU、多核环境下的硬件优势。使用多个CPU来缩短Stop The World停顿时间,部分收集器原本需要停顿JAVA线程执行的GC动作,G1收集器仍然可以通过并发的方式让JAVA程序继续执行。

2. 分代收集: G1收集器可以不需要其他的收集器配合就能独立管理整个GC堆,但他能采用不同的方式去处理新创建的对象和已经存活一段时间、熬过多次GC的旧对象。

3. 空间整合: G1从整体上看是基于”标记-整理“算法实现的收集器,从局部(两个Region之间)上看是基于”复制“算法实现的。这两种算法都意味着G1运行期间不会产生内存空间碎片,收集后可以提供规整的可用内存。

4. 可预测的停顿: G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。

        使用G1收集器时,JAVA堆的内存布局就与其他的收集器有很大的区别,它将整个JAVA堆划分为多个大小不等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,他们都是一部分Region(不需要连续)的集合。

       G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个JAVA堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先队列,每次根据允许的收集时间,优先回收价值最大的Region(这也是Garbage-First名称的由来)。这种使用Region划分空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内可以获取尽可能高的收集效率。

      在G1收集器中,Region之间的对象引用以及其他的收集器中的新生代与老年代之间的对象引用,虚拟机都是使用Remembered Set来避免全堆扫描的。G1中的每个Region都有一个与之对应的Remembered Set,虚拟机发现程序在对Reference类型的数据进行写操作时,会产生一个Write Barrier暂时中断写操作,检查Reference引用的对象是否处在不同的Region之中(在分代例子中就是检查是否有老年代中的对象引用了新生代的对象)如果是,便通过CardTable把相关的引用信息记录到被引用对象所属的Region的Remembered Set之中。当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set即可保证不对全堆扫描也不会有遗漏。

如果不计算维护Remembered Set的操作,G1收集器的运作大致可划分为一下几个步骤:

1. 初始标记:标记了一下GC Roots能直接关联到的对象,并且修改TAMS (Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这阶段需要停顿线程,但耗时很短。

2. 并发标记:从GC Roots开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时很长,但可与用户程序并发执行。

3. 最终标记:为了修正正在并发标记期间因用户程序继续运行而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最终标记阶段需要Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并发执行。

4. 筛选回收:首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,这个阶段也可以做到与用户程序一起并发执行。

       初始标记阶段仅仅只是标记了一下GC Roots能直接关联到的对象,并且修改TAMS (Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象,这阶段需要停顿线程,但耗时很短。并发标记阶段是从GC Roots开始对堆中对象进行可达性分析,找出存活的对象,这阶段耗时很长,但可与用户程序并发执行。而最终标记阶段则是为了修正正在并发标记期间因用户程序继续运行而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最终标记阶段需要Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并发执行。最后在筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,这个阶段也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。

你可能感兴趣的:(JVM)