以下为对象在内存中分配的规则,这些规则并不是百分之百固定的,其细节取决于正在使用的垃圾收集策略,当然还有与虚拟机的内存相关参数设置有关。
1. 对象优先在Eden分配
一般,对象在新生代Eden区中分配,当Eden区没有足够空间进行分配时(一般不会将新生对象直接放入Survivor中,它只保存上一次GC还存活的对象),虚拟机将发生一次Minor GC(当Eden空间不足,就会发生对新生代的GC)。
2. 大对象直接进入老年代
所谓的大对象是指,需要大量连续内存空间的Java对象,最经典的大对象就是那种很长的字符串以及数组。经常出现大对象容易导致内存还有不少空间时就提前出发垃圾收集。所以应当尽量避免短命大对象的使用。
3. 长期存活的对象将进入老年代
对象的年龄:虚拟机给每个对象定义了一个对象年龄计数器,如果对象在Eden出生并经过第一次的MinorGC后仍然存活,并且能被Survivor区中每“熬过”一次Minor GC,年龄就增加一岁,当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。
4. 动态对象年龄判定
为了更好的适应不同程度的内存状况,虚拟机并不是永远地要求对象的年龄必须达到阀值才能晋升老年代,如果Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到阀值中要求的年龄。
5. 空间分配担保
- 在发生MinorGC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象的总空间,如果这个条件成立,那么Minor GC 可以确保是安全的。
- 如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续查找老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次MinorGC是有风险的
- 如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。
触发Full GC的五种情况
- 直接调用System.gc()
- 老年代空间不足
- 永久代空间满
Java 8 中已经移除了永久代,改用元空间。元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。
Minor GC 会清理新生代(满或者无法分配大对象),Major GC是清理永久代。Full GC是清理整个堆空间——包括新生代和老年代。
- 在使用CMS策略对老年代进行垃圾回收时
- promotion failed: 进行Minor GC时,Survivor 放不下上一次存活的对象,对象只能放入老年代,而此时老年代也放不下造成的。
- concurrent mode failure:在执行CMS GC的过程中同时有对象要放入老年代,而此时老年代空间不足造成的。
- 统计得到的Minor GC 晋升到老年代的平均大小大于老年代的剩余空间
HotSpot 为了避免由于新生代对象晋升到老年代导致老年代空间不足的现象,在进行Minor GC时,做了有一个判断,如果之前统计所得的MInor GC 晋升到老年代的平均大小大于老年代的剩余空间,那么就直接触发 Full GC。