对象的内存分配,大部分在堆上分配(少部分经过JIT编译后被拆散为标量类型并间接地栈上分配),对象主要分配在新生代的Eden,如果启动了本地线程分配缓存,将按线程优先在TLAB上分配,少数情况下也可能会分配在老年代中,分配的规则不固定,取决于使用哪一种垃圾收集器组合,还有虚拟机中与内存相关的参数的设置。
1.对象优先在Eden分配
当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC(指发生在新生代的垃圾收集动作)。
虚拟机提供-XX:+PrintGCDetails这个收集器日志参数,告诉虚拟机在发生垃圾收集行为时打印内存回收日志 。
2.大对象直接进入老年代
虚拟机提供一个-XX:PretenureSizeThreshold参数(对Serial和ParNew收集器有效,Parallel Scavenge无效),令大于这个设置值的对象直接在老年代分配。
3.长期存活的对象进入老年代
虚拟机给每个对象定义一个对象年龄计数器,如果对象在Eden出生并经过第一次Minor GC后仍然存活,并能够被Survivor容纳,将被移动到Survivor空间中,并且对象年龄设置为1,对象在Survivor区每“熬过”一次Minor GC,年龄就增加1,当年龄增加到一定程度(默认15),将进入老年代。对于老年代的年龄阀值,通过-XX:MaxTenuringThreshold设置。
4.动态对象年龄判断
虚拟机不是永远要求对象的年龄必须达到MaxTenuringThreshold才能进入老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半 ,年龄大于或等于该年龄的对象就可以直接进入老年代。
5空间分配担保
在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么继续检查老年代最大可用连续空间是否大于历次进入老年代对象的平均大小,如果大于,将尝试进行一次Minor GC,如果小于,或不允许担保失败,那么要进行一次Full GC。
JDK6 Update 24之后规则改变为只要老年代的连续空间大于新生代对象总大小或历次进入老年代的平均大小就会进行Minor GC,否则将进行 Full GC。