一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情。
堆的逻辑结构
在jdk1.7及以前,堆分为新生代,老年代,永久代。如下图所示
在1.8及以后,堆分为新生代,老年代,元数据。如下图所示
新生代
新生代由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 是否设置空间分配担保