为新对象分配内存是一个非常严谨和复杂的任务。JVM的设计者们不仅需要考虑内存如何分配、在哪里分配等问题,并且由于内存分配空间算法与内存回收算法密切相关,所以还需要考虑GC执行完内存回收是否会在内存空间中产生内存碎片。
-XX:MaxTenuringThreshold =
java.lang.OutOfMemoryErro: Java heap space
小结:
1-针对幸存者S0(From/To)、S1(From/To)区,复制之后有交换,谁空谁是To区
2-关于垃圾回收:对象回收一般频繁会在新生代区收集,很少在老年代区收集,几乎不在永久代/元空间收集。
前边关于对象在内存中分配过程,可以用下边流程图表示,过程已经很详细了,不再赘述。
如果对象的Eden区出生,并经过一次Minor GC后依然存活,并且能够被Survivor区容纳的话,将被移动到Survivor区,并且对象的存活年龄设为1。
对象在Survivor区中每熬过一次MinorGC,存活年龄值就增加1,当它的年龄增加到一定的程度(默认为15岁,每个GC各不相同),就会晋升到老年代中。
对象晋升的阈值可以通过选项 -XX:MaxTenuringThreshold来设置。
针对不同年龄段的对象分配原则如下:
避免程序中出现较多的大对象。
如果survivor区间中相同年龄的所有对象的大小总和大于survivor区间的一半,年龄大于或等于该年龄的对象可以直接进入到老年代区,无需等到MasTenuringThreshold中设置的晋升年龄阈值。
举个栗子:
如对象年龄5的占30%,年龄6的占36%,年龄7的占34%,加入某个年龄段(如例子中的年龄6)后,总占用超过Survivor空间*TargetSurvivorRatio的时候,从该年龄段开始及大于的年龄对象就要进入老年代(即例子中的年龄6对象,就是年龄6和年龄7晋升到老年代),这时候无需等到MaxTenuringThreshold中要求的15。
空间分配担保目的:在年轻代进行Minor GC前,老年代本身应该还有容纳新生代所有对象的剩余空间。如果不够,则进行Full GC。
配置参数:-XX:HandlePromotionFailure 是否设置空间分配担保
- /**
- * 测试大对象直接进入老年代
- *
- * -Xmx40m -Xms40m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails
- */
- public class YoungOldAreaDemo {
- public static void main(String[] args) {
- // 创建一个20m大小的对象
- byte[] data = new byte[1024 * 1024 * 20];
- }
- }
运行前先做一下VM参数配置配置:
控制台输出结果:
因为设置总的堆内存大小只有40m,新生代空间12m,老年代空间27m,新建的20m大对象将直接晋升到老年代区间。