测试环境一个运行在 docker 中的 Java 进程 CPU 占用一直很高,即使在无请求情况下也超过 100%,负载时甚至到了 300+%,最终分析结果是 tomcat 开了热部署,即 reload=true。 (参考:https://blog.csdn.net/u010862794/article/details/80104571)下面记录分析过程:

因为程序运行在 docker 中,所以整个过程就分了两个部分:宿主机和 docker 容器中

宿主机

宿主机中运行 top 命令,查看 CPU 占用高的进程 ID,然后运行 docker container top <container> 确认高负载镜像,拿到 docker 容器 id。 

docker 容器

进入 docker 容器

docker exec -it id /bin/bash

确认进程 pid

top


拿到进程号 18

确认 nid

top -H -p 18


拿到 98,并转换为 16 进制

printf "%x\n" 98

得到 62

jstack

jstack 18|grep 0x62

使用 jstack 工具分析进程,得到结果:

"ContainerBackgroundProcessor[StandardEngine[Catalina]]" #67 daemon prio=5 os_prio=0 tid=0x00007f732c0de000 nid=0x62 runnable [0x00007f7307efd000]

已经可以判断出这就是一个 tomcat 线程,这段的完整内容如下:


上图内容使用 jstack 18 jstack.out 导出文件。

查看 tomcat server.xml 文件, context 中 reload 属性确实设置为 true,解决方案就是属性改为 false,重启镜像。

分析过程还用到了 jstat gcutil 来分析是否频繁发生 FGC,从结果上看其实没有。

jstat  -gcutil -t  18
Timestamp         S0     S1     E      O      M     CCS    YGC     YGCT    FGC    FGCT     GCT   
         3303.1  50.00   0.00  74.00  23.32  96.50  94.33   1430    9.981     0    0.000    9.981


总结

JAVA 进程 CPU 异常占用时,分析过程大概以下几个步骤:

  1. top 查询 pid
  2. top -H -p 拿到 nid
  3. jstack 命令确认异常代码
  4. jstat gcutil 分析 GC 情况

end.