一次线上CPU飙高排查实录:从Arthas到JVM调优的深入之旅

简介: 本文记录了一次线上Java应用CPU使用率异常升高的故障排查过程。通过使用阿里巴巴开源工具Arthas,快速定位到问题根源:日志切面中存在性能缺陷的正则表达式在处理超长字符串时引发“回溯爆炸”,导致CPU资源耗尽。文中详细介绍了排查步骤、问题分析及解决方案,包括利用Arthas进行实时监控、线程分析、方法监控和在线热更新修复。最后总结了排查经验与技术启示,强调工具掌握、性能意识与防御式编程的重要性。

一、 引言:风平浪静下的警报

一个平静的下午,运维平台突然弹出一条告警:线上某核心应用的某台机器,CPU使用率持续超过200%(8核机器),并且持续了十多分钟尚未恢复。

这是一个基于 Spring Boot + MyBatis 的Java应用,部署在Linux服务器上。告警就是命令,我们必须立即定位问题,避免服务雪崩。传统的“重启大法”虽然能暂时解决问题,但无法根除,我们需要找到问题的根源。

二、 排查工具与思路:为什么是Arthas?

在以往,这种排查流程通常是:

  1. top 命令找到CPU占用最高的Java进程PID。
  2. top -Hp [pid] 找到该进程下占用CPU最高的线程ID。
  3. 将线程ID转换为16进制。
  4. jstack [pid] 抓取线程快照。
  5. 在快照文件中查找对应的16进制线程ID,分析线程栈。

这个流程繁琐且一次jstack只能看到瞬间的快照,对于频繁变化的问题可能抓不到现场。

这次,我们决定使用阿里开源的Arthas,一个强大的Java诊断工具。它不仅能简化上述所有步骤,还提供了实时监控、方法执行监控、动态生成火焰图等强大功能。

三、 实战操作步骤

第一步:远程连接与附着

通过SSH登录到目标服务器,下载并启动Arthas,附着到目标Java进程上。

# 1. 找到目标Java进程(我们应用的进程)
ps -ef | grep java

# 2. 下载并启动Arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

# 3. 控制台会列出所有Java进程,输入序号选择我们要监控的进程
[INFO] arthas-boot version: 3.6.7
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 12345 org.example.MyApplication
...
输入: 1
[INFO] arthas home: /root/.arthas/lib/3.6.7/arthas
[INFO] Try to attach process 12345
...
Attach success.

第二步:全局监控,定位异常线程

使用Arthas的dashboard命令,获得一个实时的系统仪表盘。

[dashboard]

在输出的线程列表(Threads)部分,立刻发现了一个名为pool-7-thread-1的线程,其CPU使用率居高不下,持续在90%以上。这就是我们的“罪魁祸首”。

第三步:查看问题线程的详细栈

使用thread命令查看这个异常线程的完整调用栈。

# 查看CPU使用率最高的线程的堆栈信息
thread -n 3

# 或者直接指定问题线程的ID(从dashboard中获取)
thread [id]

输出结果显示,这个线程正处于RUNNABLE状态,调用栈深深地卡在java.util.regex.Pattern$CharProperty.match 方法中,而调用这个正则匹配的,是我们业务代码中的一个日志切面(LogAspect)

第四步:深入方法监控,定位问题代码

我们使用watch命令来监控这个切面中可疑方法的入参和返回值,试图找出是什么输入导致了疯狂的正则匹配。

# 监控LogAspect中cutMethod方法执行的入参
watch com.example.aop.LogAspect cutMethod '{params}' -x 3

通过观察,我们发现当某个特定的第三方接口回调我们时,会传入一个非常长的JSON字符串(超过100KB),而这个字符串中包含了大量特殊字符。我们的切面为了记录日志,会尝试对这个字符串进行匹配操作。

根源分析:
我们的日志切面里,有一段有性能缺陷的正则表达式,用于简单匹配和过滤。这个正则表达式在普通短文本下运行良好,但遇到了超长且包含复杂结构的字符串时,发生了正则表达式的“回溯爆炸”(Catastrophic Backtracking),导致CPU计算量呈指数级增长,最终耗尽了CPU资源。

有问题的正则示例(简化版):

// 为了匹配并提取内容,但分组和贪婪匹配在复杂文本下会导致大量回溯
String regex = ".*name:(.*)value:(.*)end.*";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(hugeInputString); // hugeInputString是超长字符串
if (matcher.find()) {
   
    // ...
}

四、 解决方案与优化

短期解决方案(紧急止血):

  1. 立即在Arthas中使用jad命令反编译线上类确认代码。
  2. 因为无法立即发布,我们使用了Arthas的mc(Memory Compiler)和retransform命令,在线热更新了修复后的Class文件,将正则匹配替换为简单的字符串分割split()操作。
# 1. 在本地IDE修改LogAspect.java源码并编译成Class文件
# 2. 用Arthas的mc命令编译(或直接上传.class文件)
mc -c <ClassLoaderHash> /tmp/LogAspect.java -d /tmp

# 3. 用retransform命令热加载新的字节码
retransform /tmp/com/example/aop/LogAspect.class

CPU使用率在命令执行后几分钟内迅速下降至正常水平。

长期解决方案(根本修复):

  1. 重构日志切面:移除复杂的正则表达式,对于超长内容(如length() > 1000)进行截断或跳过详细匹配,只做简单日志记录。
  2. 添加防护代码
    public void cutMethod(ProceedingJoinPoint joinPoint) {
         
        Object[] args = joinPoint.getArgs();
        for (Object arg : args) {
         
            if (arg instanceof String) {
         
                String argStr = (String) arg;
                // 对过长参数进行截断,避免耗性能操作
                if (argStr.length() > 2000) {
         
                    argStr = argStr.substring(0, 2000) + "...(truncated)";
                }
                // 使用更安全、更简单的字符串操作方法替代复杂正则
                // ...
            }
        }
        // ...
    }
    
  3. 代码审查:对全项目代码进行正则表达式扫描,避免类似性能陷阱。

五、 总结与思考

这次线上排查给我带来了几点深刻体会:

  1. 工具赋能Arthas是Java开发者不可或缺的“瑞士军刀”,它将复杂的JVM排查工作变得简单直观,极大提升了问题定位效率。掌握它,就等于拥有了线上应用的“上帝视角”。
  2. 性能意识正则表达式是一把双刃剑,编写时必须考虑其最坏情况下的时间复杂度,避免“回溯爆炸”。对于用户输入或外部传入的数据,尤其要谨慎。
  3. 防御式编程:对于日志记录、参数解析等环节,一定要对输入数据的规模和格式做必要的判断和限制,防止被异常数据“击穿”。
  4. 热更新能力:在紧急情况下,Arthas的热更新能力可以作为线上问题的救命稻草,但它毕竟是“外科手术”,最终还是要依靠完整的代码修复和发布流程。

通过这次从告警到定位再到修复的完整过程,不仅解决了一个线上故障,更对JVM应用的性能调优和问题排查建立了更深的理解。希望这次实录也能为你带来启发。

目录
相关文章
|
Arthas 监控 Java
Java 诊断利器 Arthas使用
Java 诊断利器 Arthas使用
3733 0
|
监控 Java 索引
cpu使用率过高和jvm old占用过高排查过程
cpu使用率过高和jvm old占用过高排查过程
430 2
|
Arthas 监控 Java
Jvm性能调优+监控工具Arthas【阿里开源】
Jvm性能调优+监控工具Arthas【阿里开源】
1295 0
|
Arthas 测试技术
Arthas排查生产环境CPU飚高问题
Arthas排查生产环境CPU飚高问题
347 0
Arthas排查生产环境CPU飚高问题
|
2月前
|
Arthas 监控 Java
深入理解JVM《Arthas - 阿里开源Java诊断神器》
Arthas是阿里巴巴开源的Java诊断利器,无需重启应用即可动态追踪JVM运行状态。支持实时监控、线程分析、方法追踪、类反编译、热更新及火焰图生成,集众多工具之大成,助力开发者高效定位线上问题。
|
3月前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
545 5
|
消息中间件 Java 应用服务中间件
我是如何通过火焰图分析让应用CPU占用下降近20%的
分享作者在使用Arthas火焰图工具进行Java应用性能分析和优化的经验。
|
8月前
|
Arthas 监控 Java
Arthas profiler(使用async-profiler对应用采样,生成火焰图)
Arthas profiler(使用async-profiler对应用采样,生成火焰图)
1105 10
|
7月前
|
Arthas 存储 监控
Arthas heapdump(dump java heap, 类似 jmap 命令的 heap dump 功能)
Arthas heapdump(dump java heap, 类似 jmap 命令的 heap dump 功能)
445 8
|
7月前
|
Arthas 监控 Java
Arthas dashboard(当前系统的实时数据面板)
Arthas dashboard(当前系统的实时数据面板)
388 12