概述
Java技术体系中福哦提倡的自动内存管理最终可以归结于为了自动化的解决两个问题:给对象分配内存和回收分配给对象的内存。
对象的回收在之前几篇文章中已经介绍过了;对象的内存分配,往大方向上讲,就是在堆上分配内存,对象主要分配在新生代的Eden区上,少数情况下也可能直接分配到老年代中。
(提示:Minor GC和Full GC的区别
- 新生代GC(Minor GC):指发生在新生代的垃圾收集行为,因为Java对象大多数都具备朝生夕死的特点,所以Minor GC非常的频繁,一般回收速度也比较快。
- 老年代GC(Full GC):指发生在老年代的GC,出现Full GC经常会伴随着至少一次的Minor GC,并非绝对。Full GC的速度一般会比Minor GC的速度慢10倍以上。
)
对象优先在Eden上分配
大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够的空间进行分配时,虚拟机将发起一次Minor GC
大对象直接进入老年代
所谓的大对象是指,需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串及数组。大对象对虚拟机的内存分配来说就是一个回消息(比遇到一个大对象更加坏的消息就是遇到一群短命的大对象,所以写程序时应避免在频繁调用的方法中创建大对象),经常出现大对象容易导致内存还有不少空间时就提前出发垃圾回收以获取足够的连续空间来“安置”它们。
长期存活的对象将进入老年代
虚拟机既然采用了分代收集的思想来管理内存,那内存回收时就必须能识别哪些对象应放入新生代,那些应放入老年代中。为了做到这一点,虚拟机为每个对象定义了一个年龄(Age)计数器。如果对象经过了一次Minor GC后仍然存活,并且能被Survivor区域容纳,其将被移动至Survivor空间中,并且age设置为1,对象在Survivor区中每熬过一次Minor GC后,age都会加1,当年龄达到一定程度(默认是15)时,就会晋升到老年代中。
动态对象年龄判定
为了能更好的适应不同程序的内存状况,虚拟机并不总是要求对象必须达到一定年龄才能晋升老年代,如果Survivor空间中相同年龄的对象大小超过Survivor空间的一半,年龄大于或者等于改年龄的对象就可以直接进入老年代中,无需等到特定要求的年龄。
空间分配担保
在发生Minor GC时,虚拟机会检测之前每次晋升到老年代的平均大小是否大于老年代中的剩余空间大小,如果大于,则改为直接进行一次Full GC。如果小于,则查看设置是否允许担保失败:如果允许,那只会进行Minor GC;如果不允许,则要改为进行一次Full GC。
新生代使用复制收集算法,但为了内存利用率,只使用其中一个Survivor空间来作为轮换备份,因此当出现大量对象在Minor GC后仍然存活的情况(最极端的就是内存回收后新生代中的所有对象都存活),就需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代。
取平均值进行比较其实仍然是一种动态概率的手段,也就是说,如果某次Minor GC存活后的对象突增,远远高于平均值的话,依然会导致担保失败(Handle Promotion Failure),如果出现了担保失败,那就只好再失败后重新发起一次Full GC。