JVM中堆的内存布局

280 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

堆的逻辑结构

在jdk1.7及以前,堆分为新生代,老年代,永久代。如下图所示

image.png 在1.8及以后,堆分为新生代,老年代,元数据。如下图所示

image.png

新生代

新生代由Eden和两块大小相同的Survivor(s0、s1)构成 几乎所有对象都是在新生代被创建的,新生代分为eden,s0,s1区。对象刚创建时是放到eden区的,随着eden区变满会触发新生代垃圾回收(minor GC),将新生代中没用的对象销毁,在垃圾回收中存活的对象会被移动到s0区,并且设置这些移动对象的年龄为1,再次触发minor GC时会将s0中存活的对象和eden区中存活的对象放到s1中,存活对象年龄加1,再将s0清空,下次minor GC又将对象放到s0中,周而复始。当s0或者s1中有对象年龄达到老年代年龄限制时,会将这部分对象移到老年代区。

  • 默认的老年代年龄限制是15,可以使用-XX:MaxTurningThreshold=15参数进行设置。
  • minor GC触发频繁,新生代中的80%对象都是朝生熄灭的。
  • minor GC相对Major GC(老年代gc)和Full GC(全局gc)而言回收速度比较快。
  • GC时会发生stw(stop the word),即程序停止处理。
  • 如果s0,s1放不下创建的对象则这部分对象直接升级到老年代。
  • -XX:NewRatio=2 表示老年代/新生代 = 2
  • -XX:SurvivorRatio=8 表示新生代eden/s0或者s1 为8
  • -Xmn 设置新生代内存大小(一般使用默认的就可以了)

老年代

当老年代内存不足的时候会触发老年代垃圾回收(major GC或者full GC),如果major GC后内存还是不足的情况下会报OOM。

  • Major GC速度一般比Minor GC慢十倍以上,并且STW时间更长
  • Major GC后内存还是不足则oom

对不同年龄段的对象分配原则如下所示:

  • 优先分配到eden

  • 大对象直接分配到老年代(尽量避免程序中出现过多大对象)

  • 长期存活的对象分配到老年区

  • 动态对象年龄判断:如果survivor区中相同年龄的所有对象占用内存的总和大于survivor内存的一半,年龄大于或等于该年龄的对象直接进入到老年代

  • 空间分配担保:在minorGC之前会判断新生代总空间是否小于老年代连续空闲内存的大小,如果不小于则会触发空间分配担保逻辑,参数如下所示。

      -XX:HandlePromotionFailure(jdk7及之后该参数失效)
      false:如果minor GC前新生代大于老年代剩余内存则直接fullGC
      true:如果minor GC前平均每次Minor GC的大小大于老年代剩余内存才会直接fullGC,否则会先尝试Minor GC。
    

相关jvm参数

  • -XX:+PrintFlagsInitial 查看所有参数的默认初始值
  • -XX:+PrintFlagsFinal 查看所有参数的最终值
  • -Xms 设置初始堆空间内存
  • -Xmx 设置最大堆空间内存
  • -Xmn 设置新生代大小(初始值和最大值)
  • -XX:NewRatio 配置新生代和老年代在堆中的占比
  • -XX:SurvivorRatio 设置eden和s0 s1的占比
  • -XX:MaxTurningThreshold 设置新生代对象对大年龄
  • -XX:PrintGCDetails 输出详细的gc处理日子
  • -XX:PrintGC -verbose:gc 打印gc简要信息
  • -XX:HandlePromotionFailuer 是否设置空间分配担保