对象的内存分配,主要就是堆上分配(也可能结果JIT编译后被拆散为标量类型并间接在栈上分配)。对象主要分配在新生代Eden区,如果启动了本地线程分配缓冲,将按线程优先在TLAB上分配,少数情况分配在老年代上。具体分配取决于垃圾收集器组合使用情况以及虚拟机参数设置
1. GC
1) 新生代GC(Minor GC):发生在新生代的GC,非常频繁,回收速度比较快
2) 老年代GC(Major GC/Full GC):老年代的GC,经常伴随至少一次Minor GC(并不绝对,Parallel Scavenge的收集策略就有直接Major GC的),Major GC比Minor GC慢10倍以上
2. 对象优先在Eden分配
1) 大多数情况下对象在新生代Eden区中分配
2) Eden空间不足,虚拟机会发起一次Monir GC
3) –Xmn:设置新生代大小( Xmn2g为2g),官方推荐设置为堆大小的3/8
3. 大对象直接进入老年代
1) 大对象:需要大量连续内存的java对象(例如:很长的字符串及数组)
2) 经常出现大对象就容易导致还有不少空间就提前触发垃圾收集以获取空间安置大对象
3) PretenureSizeThreshold参数设置大于这个这个值的对象直接在老年代中分配
4. 长期存活的对象进入老年代
1) 对象年龄计数器:对象在Eden区出生,第一次Minor GC后存活,被移动到Survivor空间,对象年龄设置为1;后面对象在Survivor区每熬过一次Minor GC年龄就加1;默认15岁时就被移到老年代中
2) MaxTenuringThreshold设置晋升到老年代的年龄阀值
5. 动态对象年龄判断
Survivor空间中相同年龄所有对象大小总和大于Survivor空间的一半,年龄大于等于该年龄的对象就可以直接进入老年代,无需等到年龄达到阀值
6. 空间分配担保
1) 在Minor GC时,JVM会检查晋升到老年代的平均大小是否大于老年代剩余空间大小,如果大于直接进行一次Full GC。如果小于,则查看HandlePromotionFailure设置的是否允许担保失败;如果允许则只会进行一次Minor GC;如果不允许则要一次Full GC
2) 取平均大小:因为实际回收完成前无法明确知道大小,所以只能取之前每次回收晋升到老年代对象大小的平均值作为参考
3) 当实际回收值远大于平均值,依然会导致担保失败,只能重新发起一次Full GC。虽然会有担保失败的情况,但是大多数情况会将HandlePromotionFailure开关打开,避免频繁的Full GC