# 频繁 GC
今天分享一个面试题,频繁 GC 怎么处理…
且不说为什么频繁 GC , 先聊聊你怎么发现 JVM 频繁 GC 的呢?
告警啊!对的。
第一种就是 FullGC 的告警,
第二种就是 机器 CPU 的负载过高
第三种就是 大面积的接口超时,无法处理请求。
那就好说了,咱们分析下什么地方可以进行 GC ?
堆! 但不仅仅是堆,还是堆外内存也会进行 GC 。
当然我们主要说堆!
先看一下常用的名词.

# YoungGC
YoungGC 指发生在新生代的垃圾收集动作,新生代中的对象朝生夕死,所以 Minor GC 非常频繁,回收速度也比较快。频繁的 YoungGC 是正常的,要看下 YoungGC 之后存活的对象是否有问题。
对象优先在新生代 Eden 区中分配,如果 Eden 区没有足够的空间时,就会触发一次 Young GC 。
新生代的垃圾收集器有 Serial , ParNew .
经过几次 YGC 还存活的就到了老年代。
# old GC
指发生在老年代的 GC ,速度一般比 Minor GC 慢十倍以上。
老年代的垃圾收集器有 Serial Old , Parallel Old , CMS .
老年代会在两种情况下触发 Old GC :
一是开启分配担保机制,根据历次 Minor GC 后进入老年代的对象大于当前老年代内存大小,判断 Minor GC 有风险,则会触发 Old GC ;比如:
- 1. 系统并发高、执行耗时过长,或者数据量过大,导致
young gc频繁,且gc后存活对象太多,但是survivor区存放不下(太小 或 动态年龄判断) 导致对象快速进入老年代 老年代迅速堆满 - 2. 程序一次性加载过多对象到内存 (大对象),导致频繁有大对象进入老年代
- 3. 存在内存溢出的情况,老年代驻留了大量释放不掉的对象, 只要有一点点对象进入老年代 就达到
full gc的水位了
二是Minor GC后剩余对象太多,老年代放不下了也会触发Old GC。
# Full GC
FGC 收集整个 Young Gen 以及部分 old gen 的 GC ,只有垃圾收集器 G1 有这个模式。但是我们通常说的 Full GC 指的是 针对新生代、老年代、永久代的全体内存空间的垃圾回收。
full gc 触发条件有
- 1、老年代空间不足, 所以追因的方向就是导致 老年代空间不足的原因:
大量对象频繁进入老年代 + 老年代空间释放不掉 - 2、元空间内存不足,也会触发
Full GC - 3、堆外内存
direct buffer memory使用不当导致 - 4、代码问题。老年代占用不高,重启效果不明显。这种情况,大概率是在代码里搞执行了
System.gc();
# 解决
知道了上面的问题之后,解决就简单了。
- 1、观察年轻代
gc的情况,多久执行一次、每次gc后存活对象有多少survivor区多大
存活对象比较多 超过survivor区大小或触发动态年龄判断 => 调整内存分配比例 - 2、观察老年代的内存情况 水位情况,多久执行一次、执行耗时多少、回收掉多少内存
如果在持续的上涨,而且full gc后回收效果不好,那么很有可能是内存溢出了 => 查看堆快照,定位内存溢出位置,修复代码。 - 3、如果年轻代和老年代的内存都比较低,而且频率低,那么有可能是元数据区加载太多东西了
# 最后
