对象的分配和回收流程
如上图动画所示:
1、优先在Eden区分配对象
- Eden区空间不足,触发Minor GC,标记可回收对象,然后Eden区存活对象拷贝到往Survivor-From区,接下来清空Eden区;
- 再次触发Minor GC,扫描Eden区和from区,把存活的对象复制到To区,清空Eden区和from区;
- 如果在Minor GC复制存活对象到Survivor区时,发现Survivor区内存不够,则提前把对象放入老年代;
2、大对象直接进入老年代
如果发现需要大量连续内存空间的Java对象,如很长的字符串或者数组,则直接把对象放入老年代。
可通过-XX:PretenureSizeThreshold
参数设置大对象的最小大小,该参数只对Serial和ParNew两款收集器有效。
- 因为新生代采用复制算法收集垃圾,大对象直接进入老年代,避免在Eden区和Survivor区发生大量内存复制;
- 写程序的时候尽量避免大对象。
3、长期存活对象进入老年代
固定对象年龄判断: 默认的,存活对象在Survivor的From和To区来回交换15次后,如果对象最终还是存活,就放入老年代。可以通过-XX:MaxTenuringThreshold
参数来设置对象的年龄。
动态对象年龄判断: 如果发现Survivor中有相同年龄的对象空间总和大于Survivor空间的一半,那么年龄大于或者等于该年龄的对象直接晋升到老年代。
4、空间分配担保
为什么需要分配担保:
如果Survivor区存活了很多对象,空间不够了,都需要晋升到老年代,那么就需要老年代进行分配担保,也就是将Survivor无法容纳的对象直接进入老年代。
- 发生Minor GC前,JVM先检查老年代最大可用连续空间是否大于新生代所有对象的总空间
- 大于:空间足够,直接Minor GC;
- 小于:进行一次Full GC。
空间分配担保: 如果老年代连续空间
大于新生代对象总大小
或者历次晋升的平均大小
,就会进行MinorGC
,否则进行FullGC
。
JDK 6 Update 24前会根据
HandlePromotionFailure
参数判断是否允许担保失败;
Promotion Failure
发生在MinorGC,如果Survivor区存活对象年龄到达了设定值,就会将Survivor对象晋升到老年代,如果老年代空间不足,这触发promotion failure。