Full GC 频繁怎么排查
Full GC 频繁通常说明老年代或者元空间压力比较大,也可能是代码或参数配置导致 GC 退化。
排查时先看三个东西:
|
|
如果 Full GC 之后老年代占用明显下降,比如:
|
|
说明对象大部分其实是可以回收的,更像是对象创建太快、堆太小,或者大量对象过早晋升到了老年代。
这时可以继续看:
|
|
如果 Full GC 之后老年代几乎不下降:
|
|
而且后面还在持续上涨,就要重点怀疑:
大量对象长期存活,甚至存在内存泄漏。
这时候通常需要导出 Heap Dump,分析:
|
|
另外还要关注 Full GC 的触发原因,比如:
|
|
所以 Full GC 频繁的整体排查思路可以概括成:
|
|
OOM 怎么排查
OutOfMemoryError 不只代表 Java 堆满了。
第一步应该先看 OOM 的具体类型。
常见的情况包括:
|
|
如果是:
|
|
重点看堆里的对象。
先判断是内存容量不够还是内存泄漏。
如果程序确实需要长期保存大量数据,GC 后内存可以正常下降,只是堆容量确实太小,那更像是容量问题。
如果 GC 后仍然有大量本应消失的对象存活,就要怀疑内存泄漏。
通常会在 OOM 时自动生成 Heap Dump:
|
|
然后通过 MAT 等工具分析。
主要关注:
|
|
其中最关键的是找:
为什么这个对象明明不用了,却还被某条引用链一直引用着。
例如常见的泄漏来源:
|
|
如果是 Metaspace OOM,则重点排查:
|
|
如果是直接内存 OOM,则重点看:
|
|
所以 OOM 排查的核心不是一上来就加大 -Xmx,而是:
先确定是哪块内存 OOM,再判断到底是容量不够,还是对象/资源本该释放却没有释放。
CPU 飙高怎么定位到具体线程
CPU 飙高时,首先要确定到底是不是 Java 进程占用了大量 CPU。
可以先通过系统命令找到 Java 进程:
|
|
找到对应 PID 以后,再查看这个进程内部哪些线程 CPU 占用最高:
|
|
这时可以得到一个线程 ID,例如:
|
|
接下来需要把这个线程 ID 转成十六进制,因为 jstack 里的线程通常使用十六进制 nid:
|
|
然后导出线程栈:
|
|
在里面搜索:
|
|
就可以找到这个高 CPU 线程当前正在执行什么代码。
整个流程就是:
|
|
常见的高 CPU 原因包括:
|
|
如果发现占 CPU 最高的是 GC 线程,那么问题就不一定在某一行业务代码,而可能是 GC 太频繁。
这时候就需要再结合 GC 日志、堆使用情况和对象分配速度一起判断。
所以 CPU 飙高排查的核心思路是:
先定位进程,再定位线程,再用线程栈定位代码。
如果问题和 GC 有关,再回到 GC 日志和堆分析继续排查。