JVM之堆内存结构

155 阅读4分钟

存储在JVM中的对象可以被划分为如下两类:

  • 生命周期较短的瞬时对象,此类对象的创建和消亡都非常迅速;
  • 生命周期非常长的对象,在某些极端情况下可以和JVM的生命周期一致;

由于不同对象的生命周期不同,为了更好地回收管理这些对象,优化垃圾回收性能,我们应该对堆内存进行分代,Java堆内存空间分为年轻代和老年代,其中年轻代中包含伊甸园区,幸存者0区和幸存者1区(有时称之为from区,to区)。新生代和老年代的默认比列为1:2,伊甸园区、幸存者0区和幸存者1区的默认比列为8:1:1。幸存者区中的存在是为了防止老年代很快被填满,从而增大老年代垃圾回收时间上的浪费,因为老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC要长的多,频繁进行Full GC会影响大型程序的执行和响应速度。由于新生代区垃圾回收非常频繁,采用复制算法, 一是避免空间碎片化,二是提高回收速度。由于老年代中对象存活的时间比较长,不适合采用复制算法,采用标记整理算法,将所有存活的对象都向一端移动,然后直接清理掉边界以外的内存,效率相对较高。

image.png

  1. 对象分配过程

当程序new对象后,先被分配在伊甸园区,伊甸园空间被填满时,由于需要程序再次new对象,JVM的垃圾回收器将对伊甸园区中不再被其他对象所引用的对象(未使用对象)进行销毁,再加载新的对象到伊甸园区,将伊甸园区中的剩余对象移动到幸存者0区,并设置对象年龄,如果再次触发垃圾回收,此时上次幸存下来的放到幸存者0区的,如果没有回收,就会放到幸存者1区,并设置对象年龄,如果再次经历垃圾回收,此时会重新放回幸存者0区,接着再去幸存者1区,当幸存者区中对象的年龄次数超过15时,对象便会进入老年代。

针对不同年龄段的对象分配原则:

  • 优先分配到伊甸园区
  • 大对象直接分配到老年代
  • 长期存活的对象分配到老年代
  • 动态对象判断 如果幸存者区中相同年龄的所有对象大小的和大于幸存者区大小的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无须等待新生代垃圾的要求的最大年龄,默认为15
  1. 年轻代GC(Minor GC)和老年代GC(Major GC)触发机制
  • 年轻代GC(Minor GC)

当年轻代空间不足时,就会触发Minor GC,此处年轻代空间不足指的是伊甸园区空间不足,Survivor区满不会引发GC。由于Java对象大多都具备朝生夕灭的特性,所以Minor GC将会非常频繁,回收速度也比较快。

  • 老年代GC(Major GC)

当老年代空间不足时,会先触发Minor GC,如果之后空间还不够的话,则会触发Major GC,大多数情况下出现了Major GC,经常会伴随着至少一次的Minor GC。

  1. 空间分配担保策略

在发生Minor GC之前,虚拟机会检查老年代最大可用连续空间是否大于新生代所有对象的总空间,如果大于,则此次Minor GC是安全的,如果小于,虚拟机会查看-XX:HandlePromotionFailure设置值是否允许担保失败,如果-XX:HandlePromotionFailure设置为true,那么会继续检查老年代最大可用连续空间历次晋升到老年代对象的平均大小,如果大于,那么则尝试进行一次 Minor GC,但该操作依旧是有风险的,如果小于或者XX:HandlePromotionFailure设置为false,则改为一次Full GC。