Garbage Collection -- 05 -- 分代收集算法 (Generational Collection)

原文链接:Garbage Collection – 05 – 分代收集算法 (Generational Collection)


相关文章:


接下来我们再来具体了解下分代收集算法 (Generational Collection)

分代收集算法的 GC 分为两种,一种是 Minor GC (新生代 GC),另一种是 Full GC (老年代 GC,也称为 Major GC)

  • 新生代 GC (Minor GC)

    • 新生代 GC (Minor GC),是指发生在新生代中的垃圾收集动作

    • 新生代几乎是大多数 Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在新生代中进行的,Java 中的大部分对象都具备朝生夕死的特性,所以 Minor GC 非常频繁,一般回收速度也比较快

    • 由于新生代中对象生命周期短,存活率低,所以采用复制算法,成本较低

  • 老年代 GC (Major GC / Full GC)

    • 老年代 GC (Major GC / Full GC),是指发生在老年代中的垃圾收集动作

    • 当触发了 Full GC 的时候,通常会伴随着 Minor GC,即对整个堆进行垃圾回收,这便是所谓的 Full GC (Major GC 通常是跟 Full GC 等价的)

    • 由于老年代中对象生命周期长,存活率高,没有额外空间进行分配担保,所以采用标记-清除算法标记-整理算法


一、新生代

  • 新生代的主要作用是尽可能快速地回收掉那些生命周期短的对象,分为两个区域:Eden 区和两个 Survivor 区

    • Eden 区

      • Eden 代表伊甸园,伊甸园代表着人类的起源,表示当对象刚被创建出来的时候,其内存空间首先会被分配在 Eden 区,如果 Eden 区没有足够空间进行分配时,虚拟机将会发起一次 Minor GC

      • 此外,如果 Eden 区还放不下新创建的对象的话,对象也有可能直接被放到 Survivor 区,甚至是老年代中

    • 两个 Survivor 区

      • 两个 Survivor 区,又可以分为 From Survivor 区和 To Survivor

      • 具体哪个是 From Survivor 区,哪个是 To Survivor 区是不固定的,会随着垃圾回收的进行而相互转换

    在这里插入图片描述

    • 由于新生代中的对象 98% 是 “朝生夕死” 的,所以并不需要按照 1:1 的比例来划分内存空间,而是将内存分为一块较大的 Eden 空间和两块较小的 Survivor 空间,每次使用 Eden 和其中一块 Survivor。当回收时,将 Eden 和 Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才使用过的 Survivor 空间。当 Survivor 空间不够用的时候,则需要老年代进行分配担保

    • 下面我们来看看新生代中垃圾回收的具体过程,在这之前,我们先需要了解一个名为对象年龄计数器的概念

      • 对象年龄计数器

        • 由虚拟机为每个对象进行创建,用于区分哪些对象应该放在新生代中,哪些对象应该放在老年代中

        • 如果对象在 Eden 区出生,在经过第一次 Minor GC 后仍然存活,并且能被 Survivor 区容纳的话,将会被复制到 Survivor 区中,同时将对象的年龄设置为 1

        • 对象在 Survivor 区中每 “熬过” 一次 Minor GC,年龄就增加一岁,当它的年龄增加到一定程度时 (默认为 15 岁),就会晋升到老年代中

        • 对象年龄的阈值,可以通过参数 -XX:MaxTenuringThreshold 来设置


  • 新生代垃圾回收过程演示

    • 为了方便演示,我们先忽略 Eden 区和 Survivor 区的默认大小比例,并假设每个对象大小都是一样的,Eden 区最多能保存 4 个对象,Survivor 区最多能保存 3 个对象

    • 第一次 Minor GC

      在这里插入图片描述

      • 如上所示,当Eden 区被挤满 (有一个存活对象,三个无用对象)时,会触发一次 Minor GC

      在这里插入图片描述

      • 如上所示,当触发 Minor GC 后,如果还有对象存活,且能被 Survivor 区容纳的话,则存活的对象会被复制到其中一块 Survivor 区中 (这里将其复制到 S0 区中),并且将该对象年龄设为 1

      • 此时的 S0 区为 From Survivor 区,S1 区为 To Survivor 区

      在这里插入图片描述

      • 如上所示,当复制完成后,Eden 区会被清空
    • 第二次 Minor GC

      在这里插入图片描述

      • 如上所示,当Eden 区再次被挤满 (有两个存活对象,两个无用对象)时,会触发第二次 Minor GC

      在这里插入图片描述

      • 如上所示,当触发 Minor GC 后,如果还有对象存活,且能被另一块 Survivor 区容纳的话,则会将 Eden 区中以及 S0 区中的存活对象复制到另一块 Survivor 区中 (这里将其复制到 S1区 中),同时对这些对象的年龄分别 +1

      • 此时 S1 区从 To Survivor 区变成了 From Survivor 区,S0 区从 From Survivor 区变成了 To Survivor 区

      在这里插入图片描述

      • 如上所示,当复制完成后,Eden 区以及 S0 区会被清空
    • 第三次 Minor GC

      在这里插入图片描述

      • 如上所示,当Eden 区再次被挤满 (有一个存活对象,三个无用对象)时,会触发第三次 Minor GC

      在这里插入图片描述

      • 如上所示,重复上面的步骤,Eden 区以及 S1 区中存活的对象会被复制到 S0 区中,同时对这些对象的年龄分别 +1

      在这里插入图片描述

      • 如上所示,当复制完成后,Eden 区以及 S1 区会被清空
    • 以上就是新生代中的垃圾回收过程,会周而复始地不断进行


二、老年代

  • 老年代中主要存放生命周期较长的对象

  • 当触发了 Full GC 的时候,通常会伴随着 Minor GC,即对整个堆进行垃圾回收,这便是所谓的 Full GC,严格来说的话,Full GC 表示的是对整个堆进行垃圾回收,而 Major GC 是对老年代进行垃圾回收,不过通常情况下 Full GC 和 Major GC 是等价的

  • Full GC 的速度比 Minor GC 慢的多,一般会慢 10 倍以上,但执行频率低


三、哪些对象会进入老年代

  • 新生成的大对象

    • 大对象:指的是需要大量连续内存空间的 Java 对象,最典型的大对象就是那种很长的字符串以及数组 (例如:byte[] 数组)
  • 长期存活的对象

    • 对象每经历一次 Minor GC,年龄就增加一岁,当它的年龄增加到一定程度时 (默认为 15 岁),就会晋升到老年代中
  • 动态对象年龄判断

    • 如果在 Survivor 空间中相同年龄的所有对象大小的总和大于 Survivor 空间的一半,则年龄大于或等于该年龄的对象就可以直接进入老年代,而无须等到 MaxTenuringThreshold 中要求的年龄
  • Survivor 区中存放不下的对象

    • 对象优先在 Eden 区中分配,当 Eden 区中没有足够的空间分配时,会触发一次 Minor GC,每次 Minor GC 结束后 Eden 区就会被清空,其会将依然存活的对象放到 Survivor 区中,当 Survivor 区中放不下时,则由分配担保机制进入到老年代中

四、触发两种 GC 的条件

  • Minor GC

    • Eden 区空间不足
  • Full GC

    • 老年代空间不足 ,当创建了一个大对象,如果Eden 区中放不下,就会直接放入在老年代中,如果老年代空间也不足,就会触发一次 Full GC

    • 永久代空间不足 (仅针对 JDK7 及以前的版本),当系统中需要加载的类调用的方法很多,同时永久代中没有足够的空间来存放类的元数据,就会触发一次 Full GC;在 JDK8 中,由于取消了永久代,该条件不成立,这也是为什么要用元空间替代永久代的原因之一:为了降低 Full GC 的频率,减少 GC 的负担,提升其效率

    • CMS (一款并发、使用标记-清除算法的gc) GC 时出现 promotion failed、concurrent mode failure ,对于采用 CMS 进行老年代 GC 的程序而言,尤其要注意啊 GC 日志中是否有 promotion failed、concurrent mode failure 这两种情况,当这两种状况出现的时候,可能会触发 Full GC

      • promotion failed:在进行 Minor GC 时,Survivor 区空间放不下存活的对象,对象只能放入老年代中,而此时老年代也放不下,这时候就会造成 promotion failed

      • concurrent mode failure:在执行 CMS GC 的过程中,同时有对象要放入到老年代中,而此时老年代空间不足,这时候就会造成 concurrent mode failure

      • 关于 CMS 收集器可以看这里 --> 详解CMS垃圾回收机制

    • Minor GC 后晋升到老年代对象的平均大小大于老年代的剩余空间 ,HotSpot为了避免这种情况,在进行 Minor GC 时做了一判断,如果之前统计所得到的 Minor GC 后对象晋升到老年代的平均大小大于老年代的剩余空间,那么就会直接触发 Full GC

      • 例如程序第一次触发 Minor GC 后,有 6M 的对象晋升到老年代,当下一次 Minor GC 发生时,首先会检查老年代的剩余空间是否大于 6M,如果小于 6M 则会触发一次 Full GC
    • 调用 System.gc(),该方法只是提示虚拟机需要进行垃圾回收,但是否回收则由虚拟机自行决定

    • 使用 RMI (远程方式) 来进行 RPC 或管理的 JDK 应用,每小时执行 1 次 Full GC


五、JVM 常用的性能调优参数

  • -XX:SurvivorRatio

    • Eden 区和 Survivor 区的比值,默认为 8:1
  • -XX:NewRatio

    • 老年代和新生代内存大小的比例,默认为 2:1

    • 新生代和老年代的大小通过 -Xms (初始堆大小)、-Xmx (最大堆大小) 两个参数来决定的

  • -XX:MaxTenuringThreshold

    • 对象从新生代晋升到老年代经过 GC 次数的最大阈值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值