Java设定-Xmx堆内存上限后实际可分配仅一半的原因查询
Java堆最大可分配内存仅为-Xmx设置值一半的原因分析
核心原因:分代回收的空间分配担保机制
- JVM堆默认采用分代回收设计,划分为新生代和老年代两个独立内存区域:新对象优先在新生代分配,经过多次GC仍然存活的对象会晋升到老年代存储。
- 为了避免新生代GC(Minor GC)后存活对象无法放入老年代触发异常,JVM默认开启空间分配担保规则:老年代必须预留至少等于新生代总大小的空闲空间,用于容纳Minor GC后所有可能的存活对象,这部分预留空间无法用于存储普通的长期存活对象。
你的测试代码中所有分配的1MB字节数组都被ArrayList强引用,属于全程存活的对象,最终全部会晋升到老年代存储。在默认参数配置下,老年代和新生代的大小接近1:1,因此老年代实际可用的存储存活对象的空间刚好接近总堆(-Xmx设置值)的一半。测试结果中可分配内存比总堆一半少1MB,是因为ArrayList对象本身、字节数组对象头以及Survivor区的固定占用消耗了少量内存,和你观测到的测试结果完全吻合。
验证方案
你可以通过调整JVM参数验证上述逻辑:
- 调小新生代占比:使用命令
java -Xmx512m -XX:NewRatio=10 Memory运行测试,新生代仅占总堆的1/11,可分配的内存会提升到460MB左右。 - 使用不分代GC:JDK11及以上版本可以使用ZGC收集器,执行命令
java -Xmx512m -XX:+UseZGC Memory,ZGC没有分代担保要求,可分配内存会接近512MB。
内容的提问来源于stack exchange,提问作者Henning Koehler
相关产品推荐
相关产品推荐

