• 周六. 10 月 12th, 2024

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

Java 应用中高 CPU 利用率问题的定位

King Wang

1 月 3, 2022

文章目录

  • 1. Java 应用中 CPU 峰值故障的排查
  • 2. 查找 JVM 进程
  • 3. 根据上一步中找到的 Java 主线程 ID 生成线程栈快照
  • 4. 查找出 CPU 使用率高嫌疑轻量级进程 (LWP)。
  • 5. 将高 CPU 使用率的 LWP 的 ID 由十进制转换为十六进制。
  • 6. 打开第三步拿到的线程栈快照,找到 nid 为 0x67ed 和 0x67ea 那两个线程。
  • 7. 故障排查
  • 8. 结论
  • 关于作者

1. Java 应用中 CPU 峰值故障的排查

Java 应用高 CPU 利用率可能与以下几种原因有关:

  • 垃圾收集执行 Full GC 过于频繁,但由于 servlet 容器内应用里的一个内存泄漏问题,或者 servlet 容器自身的内存泄漏问题,导致每次 Full GC 无法释放很多内存出来。
  • 由于应用需要更多内存所以垃圾回收器经常执行 Full GC (类似于前面一点所述,但可能没有内存泄漏,单单是分的内存过少)。
    译者注:类似于前面一点所述,但跟它还不一样,指的是没有内存泄漏的内存不足的情况,比如分给 JVM 的内存过少,架构或程序存在内存设计不合理等问题
  • 程序自身存在的资源竞争,长时间运行的任务,高计算代价等一些问题。

列出这些条目的目的是记载一个解决 Java 应用程序中高 CPU 使用率问题的简单流程,先看是否是 GC 相关,然后逐一排查。

2. 查找 JVM 进程

ps aux | grep java
tomcat 26551 128 30.9 3946348 1215360 ? Sl 13:43 26:21 /usr/java/latest/bin/java ........ org.apache.catalina.startup.Bootstrap start

3. 根据上一步中找到的 Java 主线程 ID 生成线程栈快照

JVM 主线程 = Linux 进程,其他 Java 线程 = 轻量级进程 (Lightweight process LWP)

4. 查找出 CPU 使用率高嫌疑轻量级进程 (LWP)。

top -H
top - 13:59:28 up 29 days, 11:15, 1 user, load average: 5.15, 4.75, 3.38
Tasks: 512 total, 8 running, 504 sleeping, 0 stopped, 0 zombie
Cpu0 : 84.9%us, 9.4%sy, 0.0%ni, 3.7%id, 0.7%wa, 0.0%hi, 1.3%si, 0.0%st
Cpu1 : 84.7%us, 8.0%sy, 0.0%ni, 5.0%id, 0.7%wa, 0.0%hi, 1.7%si, 0.0%st
Mem: 3924512k total, 3784340k used, 140172k free, 148876k buffers
Swap: 2093052k total, 0k used, 2093052k free, 1995456k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
26605 tomcat 20 0 3853m 1.1g 12m R 32.2 29.8 3:10.85 java
26602 tomcat 20 0 3853m 1.1g 12m R 14.1 29.8 1:26.46 java
26771 tomcat 20 0 3853m 1.1g 12m S 2.6 29.8 0:11.74 java
26558 tomcat 20 0 3853m 1.1g 12m S 2.3 29.8 0:06.19 java
26742 tomcat 20 0 3853m 1.1g 12m S 2.3 29.8 0:11.74 java
26792 tomcat 20 0 3853m 1.1g 12m S 2.3 29.8 0:11.82 java
26555 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:06.07 java
26556 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:05.95 java
26743 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.63 java
26744 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.89 java
26748 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.60 java
26770 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.76 java
26772 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.67 java
26776 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.51 java
26794 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.70 java
26795 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.91 java
26796 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.53 java
26800 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.70 java
26806 tomcat 20 0 3853m 1.1g 12m S 2.0 29.8 0:11.56 java

可以看到有两个 LWP 分别使用了 CPU 的 32% 和 14%,而其他的则大都保持在 2% 的这样一个水平。

5. 将高 CPU 使用率的 LWP 的 ID 由十进制转换为十六进制。

十进制 十六进制
26605 67ED
26602 67EA

6. 打开第三步拿到的线程栈快照,找到 nid 为 0x67ed 和 0x67ea 那两个线程。

....
"Dispatcher-Thread-5" daemon prio=10 tid=0x00007faee4862800 nid=0x67ed runnable [0x00007faf0f8ea000]
java.lang.Thread.State: RUNNABLE
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4556)
at java.util.regex.Pattern$Start.match(Pattern.java:3408)
at java.util.regex.Matcher.search(Matcher.java:1199)
at java.util.regex.Matcher.find(Matcher.java:592)
at java.util.regex.Matcher.replaceAll(Matcher.java:902)
at java.lang.String.replaceAll(String.java:2162)
at com.xxxxx.xxxxx.common.log4j.layout.PCIPatternLayout.format(PCIPatternLayout.java:122)
at org.apache.log4j.WriterAppender.subAppend(WriterAppender.java:302)
at org.apache.log4j.RollingFileAppender.subAppend(RollingFileAppender.java:263)
at org.apache.log4j.WriterAppender.append(WriterAppender.java:160)
at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)
- locked <0x0000000781277710> (a com.xxxxxx.xxxxxx.common.pci.log4j.DateRollingFileAppender)
at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:66)
at org.apache.log4j.AsyncAppender$Dispatcher.run(AsyncAppender.java:583)
- locked <0x0000000781276590> (a org.apache.log4j.helpers.AppenderAttachableImpl)
at java.lang.Thread.run(Thread.java:745)
...
"Dispatcher-Thread-2" daemon prio=10 tid=0x00007faee47b9000 nid=0x67ea runnable [0x00007faf0fbed000]
java.lang.Thread.State: RUNNABLE
at java.util.regex.Pattern$GroupHead.match(Pattern.java:4556)
at java.util.regex.Pattern$Start.match(Pattern.java:3408)
at java.util.regex.Matcher.search(Matcher.java:1199)
at java.util.regex.Matcher.find(Matcher.java:592)
at java.util.regex.Matcher.replaceAll(Matcher.java:902)
at java.lang.String.replaceAll(String.java:2162)
at com.xxxxxx.xxxxxx.common.log4j.layout.PCIPatternLayout.format(PCIPatternLayout.java:122)
at org.apache.log4j.WriterAppender.subAppend(WriterAppender.java:302)
at org.apache.log4j.RollingFileAppender.subAppend(RollingFileAppender.java:263)
at org.apache.log4j.WriterAppender.append(WriterAppender.java:160)
at org.apache.log4j.AppenderSkeleton.doAppend(AppenderSkeleton.java:251)
- locked <0x000000078126a9b0> (a org.apache.log4j.RollingFileAppender)
at org.apache.log4j.helpers.AppenderAttachableImpl.appendLoopOnAppenders(AppenderAttachableImpl.java:66)
at org.apache.log4j.AsyncAppender$Dispatcher.run(AsyncAppender.java:583)
- locked <0x000000078126a968> (a org.apache.log4j.helpers.AppenderAttachableImpl)
at java.lang.Thread.run(Thread.java:745)

7. 故障排查

根据前边得到的线程栈样本,貌似高的 CPU 占用率跟 log4j 的异步追加有关,在本案例中很有可能涉及 PCIPatternLayout.format() 方法。

8. 结论

两个高 CPU 使用率的 LWP 都指向了同一个原因,解决办法是将 snmp4j 升级到截止到发现问题时的最新版本,并在相关程序中将 log4j appender 配置文件中的自定义布局移除。

关于作者

Orlando.Otero.jpeg
Orlando L Otero 是联邦快递的一名软件工程师顾问,专注于系统集成,微服务,API 设计、实现以及敏捷交付。
原文链接:https://tech.asimio.net/2016/02/11/Troubleshoot-high-CPU-usage-in-Java-applications.html。

发表回复