【理解JVM】深入浅出JVM垃圾收集(HotSpot)

转载请注明原文地址:http://blog.csdn.net/mxm691292118/article/details/51006010

我把Android重难点和读书笔记都整理在github上:https://github.com/miomin/AndroidDifficulty

如果你觉得对你有帮助的话,希望可以star/follow一下哟,我会持续保持更新。


引言:我的上一篇博文提到JVM运行时的内存分块和GC的基本思想及算法,在GC的道路上越走越远,接下来要做的就是 深入浅出JVM的垃圾收集机制


一、分代垃圾回收

1、新生代:大部分刚被创建的对象被分配到这里,生命周期短,被创建后很快变成不可达。在新生代区域内发生的GC称为minor GC

新生代被划分为三个区域:

  • 1个Eden空间
  • 2个Survivor空间

节点拷贝法中,不会简单的将内存划分为1:1的两份,而是划分成1个较大的Eden和2个较小的Survivor(HotSpot中默认为Eden的1/8)。

下面看新生代的GC过程:

  • (1)大多数刚刚被创建的对象会存放在Eden。
  • (2)在Eden执行第一次GC后,幸存的对象被移动到其中一个Survivor,之后的每次Eden满了都会执行GC,且都会将幸存者移动到该Survivor,直到该Survivor满(如果这时候Survivor不足以放下来自Eden的幸存者,会使用内存分配担保,提前进入老年代)。
  • (3)当一个Survivor满了之后,将Eden和该Survivor中还存活的对象移动到另一块Survivor,清空Eden和原来的Survivor(这时候,如果Survivor的空间不足以存放所有的幸存者,会依赖老年代的内存进行分配担保,对于内存分配担保会面会详细讲解)。
  • (4)在以上的步骤中重复几次依然存活的对象,就会被移动到老年代(默认15岁被移动到老年代)。
  • (5)老年代也满了之后,会触发major GCfull GC

2、老年代:从新生代的GC中存活下来的对象,会被拷贝到老年代,老年代空间比新生代大,发生在老年代上的GC要比新生代少。老年代中发生的GC被称为major GC

3、思考一个问题:如果老年代的对象引用了一个新生代的对象,会怎么样?

  • 老年代中存在一个Card Table,所有老年代的对象指向新生代对象的引用都会被记录在表中。新生代GC时,需要查询Card Table来决定是否可以被收集,而不用查询整个老年代。
  • Card table做为一个index,每一个bit,都代表了老年代中的一块连续的区域。
  • 在Update一个bit的时候,还使用了一种叫做wirte barrier的技术,在程序修改一个ref的内容的时候,可以被编译器得知,显著的提升GC性能。
  • 注意:被老年代引用的新生代对象不会被GC,但是引用了老年代的新生代会被GC掉。

二、垃圾收集器类型

1、Serial收集器

  • 单线程收集器,会在它进行GC时,暂停其他所有的工作线程 — “Stop The World(STW)”
  • 从Serial到Parallel到CMS到G1,GC导致用户线程停顿的时间在不断缩短,但是没有完全消除。
  • 用于新生代收集

2、ParNew收集器

  • 是Serial的多线程版本
  • 只是使用多线程GC,但是跟Serial一样,在GC时也必须暂停其他工作线程。
  • 用于新生代收集
  • 不适合单CPU环境

3、Parallel Scavenge收集器

  • 与ParNew不同的是,具备自适应调节策略。

4、CMS(Concurrent Mark Sweep)收集器

  • CMS出现的目标是取得最小短的“STW”的时间,因为在耗时最长的并发标记和并发清除期间,可以与用户进程同步执行。
  • Concurrent:并发,用户线程和垃圾收集线程交替执行,而不是并行,或者说运行在不同的CPU上。

  • GC过程

    • 初始标记(需要STW):只是标记GC Roots直接关联到的对象,时间短。
    • 并发标记:进行GC Roots Tracing的过程,时间比较长。
    • 重新标记(需要STW):修正并发标记期间发送改变的对象的标记,时间也比较短。
    • 并发清除:回收死去的对象。
  • 缺点

    • (1)对CPU资源敏感:在CPU数量比较少的情况下,CMS会占用加多资源,导致用户线程执行慢。通过i-CMS改善这个情况,让GC线程和用户线程交替执行,GC过程会变慢,但是对用户线程的影响比较小。
    • (2)无法处理浮动垃圾:因为是并发的,在清理阶段,用户进程也会产生垃圾,这些垃圾无法在当次收集中被回收。所以CMS不能跟其他垃圾收集器一样,等到老年代占满后才触发GC,必须留一部分空间给浮动垃圾,如果在这个过程中浮动垃圾占满老年代剩余空间,会启动备用方案Serial收集器进行一次Full GC,停顿时间就更长了。
    • (3)内存碎片:基于标记-清除算法,所以会产生碎片,导致Full GC提前发生。为了解决该问题,CMS可以在顶不住需要Full GC前执行一个碎片整理。(但是碎片整理不是并行的,会造成STW,所以不是每次Full GC都需要碎片整理)

5、G1收集器(未来代替CMS)

  • 优势
    • 与CMS一样的并发GC
    • 不需要其他收集器配合,可以独立管理整个heap,自动协调进行分代收集(内部分配不同的Region,不需要连续的集合)
    • 用的标记-整理和节点拷贝算法,不会产生内存碎片,减少GC频率
  • 不够成熟,暂时没有实际运用

三、GC的触发条件

1、触发Minor GC(清理新生代对象)

  • 分配对象时,发现Eden区满
  • Eden区的对象进入Survivor时,发现其中一个Suvivor区满
  • 在Full GC中配置,在每次Full GC前进行一次Minor GC

2、触发Major GC / Full GC(清理老年代对象)

  • 分配对象时,发现老年代不足(CMS除外)
  • CMS清理过程中,浮动垃圾把剩余的老年代空间占满,会启动Serial收集器,触发一次Full GC
  • 老年代碎片严重,无法存入大对象,会提前触发Full GC

四、总结

1、新分配的对象进入Eden区

2、大对象直接进入老年代

  • Eden区不够,会触发GC,为了避免频繁GC,避免Eden和Survivor之间进行没必要的复制,所以考虑直接进入老年代(可以通过参数配置对象阈值)

3、长期存活的对象直接进入老年代

  • 如果在Eden中经过第一次Minor GC后还能存活,并且Survivor可以存放该对象,就将对象移入Survivor,Age设为1,在Survivor中每经过一次Minor GC,Age都会加1。(可以配置Age阈值)
  • 动态对象年龄判定:没必要一定等到Age达到阈值,如果Survivor中相同Age的对象的大小总和大于Survivor的一半,Age大于等于该Age的对象都进入老年区。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值