为什么新生代有两个Survivor区

我们都知道JVM的堆内存分为新生代(Eden + s0+ s1)和老年代

首先 我们来看一下一个方法运行时 堆内存的占用情况

  • jps  查看所有运行的项目及方法
  • jmap -heap [方法id] 查看堆占用

 

 其他参数配置 

  • 默认的,新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2 ,可以通过参数 –XX:NewRatio 配 置。
  • 默认的,Edem : from : to = 8 : 1 : 1 ( 可以通过参数 –XX:SurvivorRatio 来设定)
  • Survivor区中的对象被复制次数为15(对应虚拟机参数 -XX:+MaxTenuringThreshold)

 问题来了

为什么要分为Eden和Survivor?为什么要设置两个Survivor区? 

  • 如果没有Survivor,Eden区每进行一次轻GC,存活的对象就会被送到老年代。老年代很快被 填满,触发重GC 老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比轻GC 长得多
  • Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,Survivor的预筛选 保证,只有经历16次轻 GC还能在新生代中存活的对象,才会被送到老年代。
  • 设置两个Survivor区最大的好处就是解决了碎片化,刚刚新建的对象在Eden中,经历一次轻GC,Eden中的存活对象就会被移动到第一块survivor space S0,Eden被清空;等Eden区再满 了,就再触发一次轻GC,Eden和S0中的存活对象又会被复制送入第二块survivor space S1(这个过程非常重要,因为这种复制算法保证了S1中来自S0和Eden两部分的存活对象占用连续 的内存空间,避免了碎片化的发生)

由上边的问题又引出一个新的问题

 JVM中一次完整的GC流程是怎样的,对象如何晋升到老年代

  • Java堆 = 老年代 + 新生代
  • 新生代 = Eden + S0 + S1 当 Eden 区的空间满了, Java虚拟机会触发一次 Minor GC,以收集新生代的垃圾,存活下来的对 象,则会转移到 Survivor区。
  • 大对象(需要大量连续内存空间的Java对象,如那种很长的字符串)直接进入老年态; 如果对象在Eden出生,并经过第一次Minor GC后仍然存活,并且被Survivor容纳的话,年龄设为 1,每熬过一次Minor GC,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。即长期存 活的对象进入老年态。
  • 老年代满了而无法容纳更多的对象,Minor GC 之后通常就会进行Full GC,Full GC 清理整个内存 堆 – 包括年轻代和年老代。
  • Major GC 发生在老年代的GC,清理老年区,经常会伴随至少一次Minor GC,比Minor GC慢10 倍以上。 

推荐阅读

JVM内存结构入门

Guess you like

Origin blog.csdn.net/weixin_44912855/article/details/119923542