JVM垃圾回收解析

JVM的垃圾回收包含4个部分,如图:

 

 

一、对象存活判定

在垃圾回收之前,首选需要做的是判定对象是否存活,JVM通过可达性分析算法实现。即从GC Roots开始向下搜索对象的引用链,当一个对象到GC Roots没有任何引用链相连

时,证明此对象是不可用的。

 

二、垃圾收集算法

2.1 标记-清除算法

      算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。
      主要不足有两个:一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序  运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。

2.2 复制算法

      将可用内存划分为大小相等的两块,每次只使用其中的一块。 当这一块的内存用完了,就将还存活着的对象复制到另外一块,然后再把这一块内存一次清理掉。 每次回收整个半区的内存,内存分配时也就不用考虑内存碎片等复杂情况。只是这种算法的代价是将内存缩小为了原来的一半,代价有点高。

2.3 标记-整理算法

      标记过程与"标记-清除"算法一样,但后续步骤不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

2.4 分代收集算法

       根据对象存活周期的不同将内存划分为几块,一般是把Java堆分为新生代和老年代,这样就可以根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。 而老年代中因为对象存活率高、 没有额外空间对它进行分配担保,就必须使用“标记—清理”或者“标记—整理”算法来进行回收。

 

三、垃圾收集器

收集算法是内存回收的方法论,垃圾收集器是内存回收的具体实现。

3.1 Serial收集器

     单线程的新生代收集器,使用复制算法。"单线程"的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到  它收集结束。适合虚拟机运行在Client模式下,简单高效。

3.2 ParNew收集器

ParNew收集器是Serial收集器的多线程版本,新生代收集器,使用复制算法。

是许多运行在Server模式下的虚拟机中首选的新生代收集器,其中有一个与性能无关但很重要的原因是,除了Serial收集器外,目前只有它能与CMS收集器配合工作。

3.3 Parallel Scavenge收集器

      Parallel Scavenge收集器是一个新生代收集器,也是使用复制算法的收集器,又是并行的多线程收集器。Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量。 所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。

3.4 Serial Old收集器

      Serial Old是Serial收集器的老年代版本,同样是一个单线程收集器,使用"标记-整理"算法。可作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用。

3.5 Parallel Old收集器
      Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和"标记-整理"算法。

3.6 CMS收集器

       CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。
基于"标记—清除"算法.运作过程分为4个步骤:
初始标记:只标记GC Roots能直接关联到的对象,速度很快;
并发标记:是进行GC RootsTracing的过程;
重新标记:为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录;
并发清除:执行内存回收操作;
由于整个过程中耗时最长的并发标记和并发清除过程收集器线程都可以与用户线程一起工作,所以总体上CMS收集器的内存回收过程是与用户线程一起并发执行的。

缺点:

CMS收集器对CPU资源非常敏感;
无法处理浮动垃圾,可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生;
收集结束时会有大量空间碎片产生。

3.7 G1收集器G1是一款面向服务端应用的垃圾收集器.

G1具备如下特点:
并行与并发:G1能充分利用多核环境下的硬件优势,使用多个CPU来缩短停顿的时间。
分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新生代和和老年代的对象。
空间整合:与CMS的"标记—清理"算法不同,G1从整体来看是基于"标记—整理"算法实现,从局部看是基于"复制"算法实现的。意味着G1运作期间不会产生内存空间碎片,收集后能                    提供规整的可用内存。
可预测的停顿:降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一
                 个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
                 在G1之前的其他收集器进行收集的范围都是整个新生代或者老年代,G1将整个Java堆划分为多个大小相等的独立Region,虽然还保留有新生代和老年代的概念,但新生                   代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个Java
                 堆中进行全区域的垃圾收集。 G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根                     据允许的收集时间,优先回收价值最大的Region(这也就是Garbage-First名称的来由)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器                   在有限的时间内可以获取尽可能高的收集效率。
G1收集器的运作大致可划分为以下几个步骤:
初始标记:只是标记一下GC Roots能直接关联到的对象;
并发标记:并发标记阶段是从GC Root开始对堆中对象进行可达性分析,找出存活的对象;
最终标记:修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录;
筛选回收:首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划;

 

3.8 收集器对比

 

3.9 默认收集器

jdk7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

jdk9 默认垃圾收集器G1

posted @ 2018-09-29 16:49  havery  阅读(138)  评论(0)    收藏  举报