扫码关注微信公众号

回复“面试手册”,获取本站PDF版

回复“简历”,获取高质量简历模板

回复“加群”,加入程序员交流群

回复“电子书”,获取程序员类电子书

当前位置: Java > JVM高频面试题 > 13.JVM的内存分配与回收

Java的自动内存管理主要解决了给对象分配内存回收分配给对象的内存两个问题,先来看下Java虚拟机是如何为对象分配内存的

Java对象的内存分配主要就是在堆上,Java堆的基本结构如下,大体上可以分为新生代老年代

Java堆
Java 堆

新生代默认占1/3,老年代默认占2/3,新生代包含Eden区、From Survivor0区和 To Survivor1区,默认比例是8:1:1,老年代就一个Old Memory区。

一般情况下是这样分配的

  • 对象先在Eden区分配,当Eden区没有足够的空间去分配时,虚拟机会发起一次Minor GC,将存活的对象放到From Survivor区(对象年龄为1)
  • 当再次发生Minor GC,会将Eden区和From Survivor区一起清理,存活的对象会被移动到To Survivor区(年龄加1)
  • 这时From Survivor区会和To Survivor区进行交换,然后重复第一步,不过这次第一步中的From Survivor区其实是上一轮中的To Survivor区。

每次移动,对象的年龄就会加1,当年龄到达15时(默认是15,对象晋升老年代的年龄阈值可以通过参数-XX: MaxTenuringThreshold设置),会从新生代进入老年代。

下面介绍几种收集器的内存分配策略

  • 对象优先在Eden区分配
  • 大对象直接进入老年代(大对象指需要大量连续内存空间的Java对象)
  • 长期存活的对象进入老年代

注:为了可以更好地适应不同程度的内存状况,虚拟机并不是必须要求对象的年龄达到MaxTenuringThreshold才进入老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象可以直接进入老年代,无需等到MaxTenuringThreshold

最后说下空间分配担保机制,在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果大于,则表明Minor GC可以安全进行。如果不大于,虚拟机会查看HandlePromotionFailure设置是否允许担保失败。如果允许,则会继续检查老年代的最大可利用连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,则会尝试进行一次Minor GC(存在一定风险),如果小于或者HandlePromotionFailure设置不允许担保失败,则这一次会进行Full GC。

这里解释为什么会存在风险,因为在新生代使用的垃圾收集算法是复制算法,前面也提到了,只有一个Survivor空间作为轮换备份,如果这时出现大量对象在Minor GC后仍然存活,则需要老年代进行担保,Survivor无法容纳的对象会直接进入老年代,风险就是Survivor无法容纳的对象有多大很难确定,也就无法保证老年代的空间一定够用,一般是取之前每一次回收晋升到老年代对象的平均大小作为参考值。

说完了空间分配担保机制的概念,不知道大家看出来这玩意儿有什么用了吗?

其实很简单,就是怕Minor GC后需要进入到老年代的对象太多了,老年代没有那么大空间,先提前检查一下,如果检查结果显示老年代确实装不下,那么这次Minor GC就得改成Full GC,那Full GC完了老年代空间还是不够呢?那会OOM内存溢出的


点击面试手册,获取本站面试手册PDF完整版