天天看点

谈谈对垃圾回收的理解

垃圾回收机制—如何判断对象已死

(1)引用计数算法:不能解决循环引起的问题

(2)可达性分析算法(Java采用):GC Roots 、 GC Roots引用链。对象到GC Roots不可达时,就是可回收对象。

垃圾回收内存划分

谈谈对垃圾回收的理解

Minor GC非常频繁,一般回收速度也比较快

Major GC的速度一般比Minor GC慢10倍以上

Full GC:在不同的语义条件下,对Full GC的定义也不同,有时候指老年代的垃圾回收,有时候指全堆(新生代+老年代)的垃圾回收,有可能指有用户线程暂停(Stop-The-World)的垃圾回收(如GC日志中)

垃圾回收线程

谈谈对垃圾回收的理解

垃圾回收线程抽象出做垃圾回收工作的对象:垃圾回收器JVM提供了多种垃圾回收器,每种垃圾回收器,在垃圾回收工作时,采用不同的垃圾回收算法

垃圾回收算法

标记-清除算法(Mark Sweep)

  1. 老年代算法
  2. 标记、清除阶段
  3. 特性:效率问题和空间碎片问题

    (1)效率问题,标记和清除两个过程的效率都不高

    (2)空间问题,标记和清除之后会产生大量不连续的内存碎片,空间碎片太多可能导致以后在程序运行过程中需要分配较大对象时,无法找到较大足够的连续内存而不得不提前触发另一次垃圾收集动作

复制算法(copying)

  1. 新生代算法
  2. 谈谈对垃圾回收的理解

    (1)S0中复制灰色的存活对象到S1(回收前:S0存放对象,S1留空)

    (2)清空S0(回收后:S1存放对象,S0留空)

  3. 内存使用率不高,只有50%

标记-整理算法(Mark Compact)

  1. 老年代算法
  2. 和标记-清除算法相比,没有清除阶段,而是移动存活对象+整理非存货对象的内存
    谈谈对垃圾回收的理解
    新生代基于复制算法的回收过程,S区基于分配担保机制复制存货对象

STW:用户线程暂停,造成用户看起来进程卡顿的情况(执行垃圾回收工作)

吞吐量、用户体验、用户线程暂停(单次暂停时间、总的暂停时间)—基于相同的代码,在同样的时间内执行统计不同次数垃圾回收的结果。单次暂停时间和总的暂停时间,统计结果一般是反比

吞吐量:算暂停总时间,越长吞吐量越低

用户体验:算单次暂停时间,越长,用户体验越不好

结果:吞吐量和用户体验是反比

垃圾收集器

谈谈对垃圾回收的理解

七种垃圾收集器

(1)Serial(串行GC)—复制算法

(2)ParNew(并行GC)—复制算法

(3)Parallel Scavenge(并行回收GC)—复制算法

(4)Serial Old(MSC)(串行GC)—标记-整理算法

(5)CMS(并发GC)—标记-清除算法

(6)Parallel Old(并行GC)—标记-整理算法

(7)G1

1~3用于年轻代垃圾回收:年轻代的垃圾回收称为minor GC

4~6用于年老代垃圾回收(当然也可以用于方法区的回收):年老代的垃圾回收称为full GC

G1独立完成"分代垃圾回收"

前5种垃圾收集器都会暂停用户线程

吞吐量优先的场景:Parallel Scavenge + Parallel Old

用户体验优先的场景:ParNew + CMS 和 G1

CMS

标记-清除算法,用户体验优先

  1. 初始标记:初始标记只是标记GC Roots能直接关联的对象,速度很快,需要STW
  2. 并发标记:并发标记阶段就是进行GC Roots Tracing的过程
  3. 重新标记:重新标记阶段是为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记时间短,仍需要STW
  4. 并发清除:并发清除阶段会清除对象

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

缺陷

  1. 抢占CPU资源:比如3个用户线程,其他系统线程有7个,没有垃圾回收线程时的用户线程调度率为3/10,如果假如5个垃圾回收线程,就变成了1/5.
  2. 无法处理浮动垃圾,出现Concurrent Mode Failure,导致另一次老年代垃圾回收(使用Serial Old再次回收老年代)

    (1)浮动垃圾:CMS第四阶段并发清除阶段用户线程产生对象,进入老年代,里面的可回收对象

    (2)什么时候会导致错误?老年代内存无法满足新进入的对象

    (3)解决方案:serial old再次major gc

  3. 空间碎片化问题

G1

整体为标记-整理算法,局部为复制算法,用户体验优先

内存划分的方式

用在heap memory很大的情况下,把heap划分为很多很多的region块,然后并行的对其进行垃圾回收。无论如何,G1收集器采用的算法都意味着一个region可能属于Eden,Survivor或者Tenured内存区域(动态指定region为E区/S区/T区)

新生代GC:

将E区/S区存活对象,复制到某个region(空白region动态指定为S区)

将E区/S区满足进入老年代的对象,复制到某个region(空白region动态指定为T区)

可以有多个E区、S区和T区

老年代GC:

初始标记:和CMS不同的是,可以和minor gc同时执行

并发标记:Tenured region中对象的存活率很小或者没有对象存活,那么G1就会在这个阶段将其回收掉,而不用等到后面的clean up阶段,这也是Garbage First名字的由来

最终标记:和CMS第三阶段算法不同

筛选回收:Clean up/Copy,同样可以和minor gc同时执行