- JVM:
1. 首先使用top -c 或ps -ef | grep java 命令找到Java的进程PID。
2. 再使用top -p [pid] -h 命令找到进程内CPU飚高的线程。
3. 通过jstack -l [pid] 命令获得进程的堆栈记录。
4. 在记录中搜索线程ID,观察线程状态(Runnable,Block?)线程执行的方法调用链,是否有死循环或耗时长的超时操作。
5. 使用jstat -gcutil工具观察进程的gc历史
观察FGC次数表示是否频繁出现Full GC,且耗时较高。 或者GC后老年代占用不下降。可能出现内存泄漏
6. 使用jmap -heap [pid] 查看堆内存,jmap -histo查看对象分布
- Arthas:
- thread -n 3: 显示CPU占用前三的线程
- thread [pid] 显示线程堆栈
- trace 类名+方法名 : 追踪方法耗时
- profile start/stop 生成火焰图, 例如找到最宽的山峰(耗时最久的堆栈)然后从上往下分析代码。观察堆栈:
- 如果频繁出现while/for代表可能出现代码死循环,复杂度过高
- 如果频繁出现synchronize/ monitor enter等字段,可能存在死锁,锁竞争
总结:
如果出现CPU占用高+线程Runable状态: 可能存在代码死循环,复杂度过高的场景,需要根据代码堆栈中的调用链排查方法。
如果出现CPU占用高+线程Block状态: 可能存在锁竞争导致的死锁,需要根据代码堆栈中的调用链排查方法,进而检查等待的锁和持有锁的线程为什么没有释放。
如果出现CPU占用高+频繁GC的场景:可能出现内存泄漏,需要根据jmap查看堆内存和对象分布
----------
排查OOM问题:
在JVM中设置OOM时自动记录heap dump文件,拿到heap文件后使用jProfiler分析。 重点关注:
- 大对象
- 异常多的对象
- 不应该存在的对象(内存泄漏)
观察如果:
-
对象数量随时间持续增加:可能存在内存泄漏
-
存在特别大的对象:大对象需要优化数据结构。
-----
题外话: 内存泄漏排查
如果发现代码存在内存泄漏,可能是以下几个场景:
- 没有正确的关闭资源,如果流,数据库链接等
- 使用了HashMap来实现缓存,但是没有清理策(可以自定义LRU清理策略,或者使用WeakHashMap)
- 存在静态集合类,例如static ArrayList,该集合中的对象是永远不会被回收的。需要避免使用静态集合类