内存分配
内存分配策略
- 优先分配到Eden区
- 大对象直接分配到老年代
- 长期存活的对象分配到老年代
- 空间分配担保(新生代向老年代借内存)
- 动态对象年龄判断
优先分配到Eden区
通过代码验证,首先我们先设置各分区内存大小(测试类右键→Run As→Run Configurations→Arguments),然后再分情况测试
-verbose:gc -XX:+PrintGCDetails -XX:+UseSerialGC -Xms20M -Xmx20M -Xmn10M -XX:SurvivorRatio=8
通过配置后,内存各分区大小如下图
1. Eden区内存充足
2. Eden区内存不足时(完全不能满足)
3. Eden区内存不足时(能满足部分对象)
大对象直接分配到老年代
通过设置-XX:PretenureSizeThreshold=6M来指定内存占用6M及以上的对象被看作大对象
长期存活的对象分配到老年代
-XX:MaxTenuringThreshold=10
复制算法中存活对象被复制的次数可以被称作对象的年龄,当年龄达到指定数量时对象就会被放到老年代中,那么指定数量就是通过-XX:MaxTenuringThreshold指定,不指定的时候默认值时15。
空间分配担保(新生代向老年代借内存)
-XX:+HandlePromotionFailure将空间分配担保置为启用状态
-XX:-HandlePromotionFailure将空间分配担保置为禁用状态
我们知道,当堆区中的新生代内存不够用的时候,会向老年代借用内存,借用内存就会存在风险。所以每次Minor GC执行前虚拟机都会检查老年代中最大的连续可用空间是否大于新生代所有对象的总空间,若大于,则证明此次Minor GC是安全的,若不大于,则虚拟机会检查HandlePromotionFailure是否允许担保失败,若允许失败,则虚拟机会检查老年代最大的连续可用空间是否大于历次晋升到老年代的对象平均大小,若大于,执行Minor GC(此次执行存在风险),若不大于,或HandlePromotionFailure不允许失败,则执行Full GC。为了避免Full GC过于频繁,所以一般都会启用空间分配担保,JDK5及之前默认关闭空间分配担保,JDK6及之后已经默认开启了空间分配担保。
动态对象年龄判断
为了能更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或者等于该年龄的对象直接可以进入老年代,无须等到MaxTenuringThreshold中要求的年龄.
逃逸分析与栈上分配
逃逸分析的目标是分析对象作用域;当对象的作用域仅在当前方法中有效,则对象没有发生逃逸。虚拟机会将没有发生逃逸的对象会被直接放到栈中。随着方法执行完毕,栈帧释放而被回收。