GC总结
Minor GC Major GC FullGC
- Minor GC 新生代GC
- Major GC 老年代GC
- Full GC 整堆收集,收集整个java堆和方法区的所有垃圾
因为STW问题的存在,尽量避免垃圾回收
Major GC 和Full GC出现STW的时间往往是Minor GC的10倍以上 所以尽量避免出现Major GC和Full GC的调用
- JVM在进行GC时,并非每次都对上面三个内存区域一起回收,大部门时间回收是在新生代.针对HotSpot虚拟机,他里面的GC按照回收区域又分为两大类型
- 部分收集Partial GC
不收集整个Java堆空间
新生代的收集 Minor GC /Young GC 只是新生代的垃圾收集
老年代的收集 Major GC / Old GC 只是老年代的垃圾收集
- 目前只有CMSGC会有单独收集老年代的行为
- 注意很多时间Major GC会和Full GC混淆,需要具体分辨是老年代回收还是整堆回收
混合收集 Mixed GC 整个新生代和部分老年代的垃圾手机
- 目前只有G1 GC会有只有行为
- 整堆收集 Full GC 整个java堆和方法区的垃圾收集
Minor GC
- 当新生代空间不足,会触发MinorGC,这里新生代具体只的是Eden满,Survivor满不会触发GC
- 因为java对象大多数具备朝生夕死的特性,所以Minor GC会非常的频繁,一般回收的速度也比较快
- Minor GC也会引发STW 暂停其他用户进程,等垃圾回收结束,用户线程才恢复运行
Major GC
- 指发生在老年代的GC,对象从老年代消失时,我们说Major GC 或者 Full GC发生了
- 出现MajorGC 通常至少会有一次Minor GC (但非绝对,在Parallel Scavenge收集器的收集策略里面有直接进行MajorGC的策略选择过程)
- 在老年代空间不足的时候会先尝试MinorGC,如果之后空间还不足,触发Major GC
- Major GC的速度比Minor GC慢10倍以上,STW时间更大,如果Major GC之后内存还不足就触发OOM
Full GC
- 触发Full GC的情况有五种
- 显示调用System.gc() 系统建议执行FullGC,但不一定执行
- 老年代空间不足
- 方法区空间不足
- 通过Minor GC后进入老年代的平均大小 大于老年代的可用内存
- 由Eden Survivor Space(From) 向Survivor Space (To)复制时,对象大小大于To的可用内存,则把该对象转存到老年代,但此时老年代的内存小于该对象大小
图解
public class HeapTest3 {
public static void main(String[] args) {
int i = 0;
try {
List<String> list = new ArrayList<>();
String a = "mogu blog";
while(true) {
list.add(a);
a = a + a;
i++;
}
}catch (Exception e) {
e.getStackTrace();
}
}
}
- 第一次Eden满之后触发GC 向数据流向Survivor TO 和Old区
- 第二次Eden满 触发Minor GC与上次操作相同
- 经历2次GC之后老年代满了触发了一次FullGC,清空了新生代的数据,对Old区的数据进行清理
- 第三次Major GC尝试处理,但此时因为字符串过长新生代已经无法存下了.直接尝试放入Old区
- 第二次Full GC Old区已经放不下了触发FullGC之后发现还是放不下抛出OOM异常
堆空间分代思想
为什么要把java堆分带?不分带就不能正常工作?经研究,不同对象的生命周期不同.70%-99%的对象都是临时对象.
新生代: 有Eden和两块大小相同的Survivor (FROM/TO) TO总为空
老年代: 存在新生代经理多次GC仍然存活的对象
- 也可以不分带,分带的唯一目的就是优化GC的性能,尽量减少GC带来的STW
内存分配策略
- 如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,会被移动到Survivor空间,并且将对象年龄设为1.对象在Survivor区每经过一次Minor GC之后仍然存活的话,年龄就会增加1,当他年龄达到一定程度(HotSpot默认15)时会晋升到老年代
- 对象晋升到老年代的年龄阈值 设置 -XX:MaxTenuringThreshold
- 针对不同年龄段对象的分配原则
- 优先分配到Eden
- 开发中比较长的字符串或者数组,会直接存放在老年代,但是因为新创建的对象都是朝生夕死的,所以这个大对象可能也很快需要被回收,但因为老年代Major GC触发的次数比Minor GC少,所以回收起来就会比较慢
- 大对象直接分配到老年代
- 尽量避免程序中出现过多的大对象
- 长期存活的对象分配到老年代
- 动态对象年龄判断
- 如果Survivor区中相同年龄的所有对象大小之和大于了Survivor空间的一般,此时年龄大于或等于该年龄的对象可以直接进入老年代 无需等到MaxTenuringThreshold
- 空间分配担保: -XX:HandlePromotionFailure
- 也就是经过Minor GC后,所有对象都存活,因为Survivor比较小,所有需要将Survivor无法接纳的对象保存到老年代中
- 优先分配到Eden