注:本博客内容来源于《深入理解java虚拟机》
Minor GC和Full GC有什么不一样?
- 新生代GC(Minor GC): 指发生在新生代的垃圾回收动作,因为java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。
- 老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC,经常会伴随至少一次Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。
内存分配与垃圾回收策略:
- 对象优先在Eden分配,下面使用代码来说明
public class MinorGC {
private static final int _1MB = 1024*1024;
/**
* -Xms20M -Xmx20M : 堆的最小值为20M, 最大值为20M, 可以限制java堆的大小为20MB
* -Xmn10M : 新生代分配10MB, 剩下10MB分配给老年代
* -XX: +PrintGCDetails : 虚拟机提供的查看垃圾日志参数
* -XX: SurvivorRatio=8 : 决定了新生代Eden区与一个Survivor区的空间比例是8:1
*/
public static void testAllocation(){
byte[] allocation1, allocation2,allocation3,allocation4;
allocation1 = new byte[2 * _1MB];
allocation2 = new byte[2 * _1MB];
allocation3 = new byte[2 * _1MB];
//出现一次Minor GC
allocation4 = new byte[4 * _1MB];
}
public static void main(String[] args) {
testAllocation();
}
}
idea虚拟机参数配置,参数说明见代码注释
控制台打印的日志参数:
原理分析:
-
1,从输出结果可以看出"eden space 8192k, from space 1024k, to space 1024k",
新生代总的可用空间为8192k+1024k=9216k,Survivor区的另一个空间是用来保存复制的java对象,不可用。 -
2,代码执行到allocation4时,Eden区内存不够,会触发一次Minor
GC,新生代由7812k变为1010k,但是总内存占用量则几乎没有减少(虚拟机没有找到可回收的对象)。这次GC发生的原因是Eden区已经被占用6MB,剩下的3M不足以分配allocation4所需的4MB内存。GC期间虚拟机发现Eden区的6MB对象无法放入另1个Survivor空间(只有1MB),所有只好通过分配担保机制提前转移到老年代(10MB)。 -
3,这次GC结束后,4MB的allocation4对象顺利分配到Eden中,因此程序执行完的结果是Eden占用4MB(allocation4占用),Survivor空闲,老年代被占用6MB(被allocation1,allocation2,
allocation3占用)。
Minor GC和Full GC的触发条件:
- Minor GC: 当Eden区满了的时候,新进来的对象不能保存时会触发Minor GC;
- Full GC:
- Perm空间不足;
- CMS GC时出现promotion failed和concurrent mode failure(concurrent mode failure发生的原因一般是CMS正在进行,但是由于老年代空间不足,需要尽快回收老年代里面的不再被使用的对象,这时停止所有的线程,同时终止CMS,直接进行Serial Old GC);
- 统计得到的Young GC晋升到老年代的平均大小大于老年代的剩余空间;
- 主动触发Full GC(执行jmap -histo:live [pid])来避免碎片问题。
CMS之promotion failed和concurrent mode failure的区别:
- promotion failed:该问题是在进行Minor GC时,Survivor Space放不下,对象只能放入老年代,而此时老年代也放不下造成的;promotion failed时老年代CMS还没有机会进行回收,又放不下转移到老年代的对象。
- concurrent mode failure:在执行CMS GC的过程中同时业务线程将对象放入老年代,而此时老年代空间不足;或者在做Minor GC的时候,新生代Survivor空间放不下,需要放入老年代,而老年代也放不下而产生的。需要尽快回收老年代里面的不再被使用的对象,这时停止所有的线程,同时终止CMS,直接进行Serial Old GC。