各位代码战士,在生产环境战场中,常遇到诡异的「代码卡顿诅咒」—— 程序无报错,但响应时间突然从 100ms 飙升到 5s,CPU 使用率飙升至 90% 以上,如同战士作战时突然手脚僵硬,无法正常行动。此时若重启服务,虽能暂时缓解,但无法根治,下次仍可能复发。

而 Arthas(阿尔萨斯)正是破解此诅咒的「诊断镜黑魔法」—— 无需重启服务,无需修改代码,就能实时窥探 JVM 内部状态、定位卡顿根源。今天,吾将带各位掌握 Arthas 的核心诊断咒文,3 分钟内揪出生产环境代码卡顿的元凶!

一、Arthas 诊断镜:生产环境的「透视眼法器」

Arthas 是阿里巴巴开源的 Java 诊断工具,如同给 JVM 装上「透视镜」,支持:

  • 查看 JVM 内存、CPU、线程状态(如同观察战士的体力、心率);

  • 追踪方法执行耗时(定位哪段代码拖慢响应);

  • 查看方法参数、返回值、异常信息(无需加日志);

  • 反编译 class 文件(验证生产环境代码是否为最新版本)。

其核心优势是「无侵入式诊断」—— 无需重启服务,无需在代码中埋点,直接附着到运行中的 Java 进程,如同战士在战斗中实时体检,不影响正常作战。

二、战前准备:召唤 Arthas 诊断镜

1. 下载 Arthas 法器

在生产服务器上执行以下命令,下载 Arthas 压缩包(或直接下载arthas-boot.jar):

# 下载Arthas压缩包(轻量版,仅核心诊断咒文)

curl -O https://arthas.aliyun.com/arthas-boot.jar

# 或下载完整压缩包(包含文档、示例咒文)

curl -O https://arthas.aliyun.com/dist/arthas-packaging-3.7.2-bin.zip

unzip arthas-packaging-3.7.2-bin.zip

2. 附着到目标 Java 进程

执行以下命令,启动 Arthas 并选择要诊断的 Java 进程(即卡顿的业务服务进程):

# 启动Arthas诊断镜

java -jar arthas-boot.jar

启动后,Arthas 会列出当前服务器上所有 Java 进程,格式如下:

[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.

* [1]: 1234 com.codewarrior.order.service.OrderApplication(卡顿的订单服务进程)

[2]: 5678 com.codewarrior.user.service.UserApplication

输入卡顿进程对应的序号(如1),按下回车,Arthas 会成功附着到目标进程,此时会显示诊断镜启动成功的提示:

[INFO] arthas home: /root/.arthas/lib/3.7.2/arthas

[INFO] Try to attach process 1234

[INFO] Attach process 1234 success.

[INFO] arthas-client connect 127.0.0.1 3658

,---. ,------. ,--------.,--. ,--. ,---. ,---.

/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'

| .-. || '--'.' | | | .--. || .-. . -.

| | | || |\ \ | | | | | || | | |.-' |

--' ----' '--' --' --' ----' --'`-----'

至此,Arthas 诊断镜已成功激活,接下来开始破解「代码卡顿诅咒」!

三、第一步:探查卡顿根源 —— 线程状态「透视咒」

代码卡顿的核心原因通常是「线程阻塞」或「线程占用 CPU 过高」,如同战士被敌人牵制无法行动,或体力过度消耗导致行动迟缓。通过 Arthas 的thread命令,可实时查看线程状态,定位异常线程。

1. 查看线程整体状态(快速扫描)

执行以下命令,查看所有线程的 CPU 占用率、状态(如 RUNNABLE/WAITING/BLOCKED):

# thread -n 5:查看CPU占用率前5的线程(高频卡顿线程常在此列)

thread -n 5

执行后会输出类似结果:

Threads Total: 156, NEW: 0, RUNNABLE: 8, BLOCKED: 2, WAITING: 120, TIMED_WAITING: 26, TERMINATED: 0

CPU usage top 5 threads:

1. Thread-10 (CPU: 85%) - com.codewarrior.order.service.impl.OrderCalculateServiceImpl.calculateDiscount(OrderCalculateServiceImpl.java:45)

2. Thread-8 (CPU: 78%) - com.codewarrior.order.service.impl.OrderCalculateServiceImpl.calculateDiscount(OrderCalculateServiceImpl.java:45)

3. Thread-12 (CPU: 72%) - com.codewarrior.order.service.impl.OrderCalculateServiceImpl.calculateDiscount(OrderCalculateServiceImpl.java:45)

4. http-nio-8080-exec-1 (CPU: 5%) - org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:624)

5. http-nio-8080-exec-2 (CPU: 4%) - org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:624)

关键发现:前 3 个线程 CPU 占用率均超过 70%,且都指向OrderCalculateServiceImpl.java:45—— 这大概率是卡顿诅咒的源头!

2. 查看异常线程详情(深度剖析)

针对 CPU 占用过高的线程(如 Thread-10),执行以下命令,查看线程的调用栈,定位具体卡顿代码行:

# thread 线程ID:查看指定线程的详细调用栈

thread 10

执行后会输出线程的完整调用栈,重点关注「at」开头的代码行:

"Thread-10" Id=10 RUNNABLE

at com.codewarrior.order.service.impl.OrderCalculateServiceImpl.calculateDiscount(OrderCalculateServiceImpl.java:45)

at com.codewarrior.order.service.impl.OrderCalculateServiceImpl.calculateTotalPrice(OrderCalculateServiceImpl.java:28)

at com.codewarrior.order.service.impl.OrderServiceImpl.createOrder(OrderServiceImpl.java:63)

at com.codewarrior.order.controller.OrderController.createOrder(OrderController.java:35)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:498)

结论:线程 10 在执行calculateDiscount方法的第 45 行时出现卡顿,需进一步查看该方法的代码逻辑。

四、第二步:追踪方法耗时 —— 执行链路「追踪咒」

找到可疑方法后,通过 Arthas 的trace命令,可追踪方法的执行链路,查看每个子方法的耗时,定位具体拖慢性能的代码块(如同战士拆解敌人的攻击动作,找到破绽)。

1. 追踪可疑方法的执行链路

执行以下命令,追踪OrderCalculateServiceImpl类的calculateDiscount方法,查看每个子方法的耗时:

# trace 类名 方法名 -n 5:追踪方法5次调用,输出子方法耗时

trace com.codewarrior.order.service.impl.OrderCalculateServiceImpl calculateDiscount -n 5

其中:

  • com.codewarrior.order.service.impl.OrderCalculateServiceImpl:可疑类的完整路径;

  • calculateDiscount:可疑方法名;

  • -n 5:仅追踪 5 次方法调用(避免输出过多日志)。

执行后,当有请求调用calculateDiscount方法时,会输出类似结果:

`---[1200ms] com.codewarrior.order.service.impl.OrderCalculateServiceImpl:calculateDiscount()

+---[5ms] com.codewarrior.order.entity.Order:getItems()

+---[1180ms] com.codewarrior.order.service.impl.OrderCalculateServiceImpl:calculateItemDiscount()

| +---[1175ms] com.codewarrior.product.service.remote.ProductRemoteService:getProductPrice()

| | +---[1170ms] feign.SynchronousMethodHandler:invoke()

| | +---[2ms] com.codewarrior.common.utils.JsonUtils:toJson()

| +---[3ms] com.codewarrior.order.entity.OrderItem:setDiscountPrice()

+---[10ms] com.codewarrior.order.entity.Order:setTotalDiscount()

关键发现:calculateDiscount方法总耗时 1200ms,其中getProductPrice远程调用耗时 1170ms—— 这是导致卡顿的核心原因!该远程调用可能因网络延迟、产品服务响应慢,拖慢了整个订单计算流程。

五、第三步:验证方法参数与返回值 —— 数据「洞察咒」

若怀疑远程调用的参数异常(如传入无效的商品 ID 导致服务处理缓慢),可通过 Arthas 的watch命令,查看方法的入参、返回值和异常信息,验证数据是否正常(如同战士用望远镜观察敌人的武器装备,判断是否有异常)。

1. 查看方法的入参与返回值

执行以下命令,查看ProductRemoteService的getProductPrice方法的入参和返回值:

# watch 类名 方法名 "{params,returnObj,throwExp}" -n 3

watch com.codewarrior.product.service.remote.ProductRemoteService getProductPrice "{params,returnObj,throwExp}" -n 3

其中:

  • {params,returnObj,throwExp}:指定输出内容(params = 入参,returnObj = 返回值,throwExp = 异常信息);

  • -n 3:仅查看 3 次方法调用。

执行后,当方法被调用时,会输出类似结果:

ts=2025-11-10 14:30:00; [cost=1170ms]

params=[10086](入参:商品ID=10086)

returnObj={"productId":10086,"price":99.9,"stock":100}(返回值:商品价格99.9元)

throwExp=null(无异常)

结论:入参和返回值均正常,排除参数异常问题,卡顿原因确认为远程调用耗时过长(可能是产品服务自身性能问题,或网络延迟)。

六、第四步:反编译生产代码 —— 版本「验证咒」

若怀疑生产环境的代码不是最新版本(如本地修复的性能问题未上线),可通过 Arthas 的jad命令,反编译生产环境的 class 文件,验证代码逻辑是否正确(如同战士检查自己的武器是否为最新锻造版本)。

1. 反编译可疑类

执行以下命令,反编译OrderCalculateServiceImpl类,查看第 45 行的代码逻辑:

# jad 类名:反编译指定类

jad com.codewarrior.order.service.impl.OrderCalculateServiceImpl

执行后会输出反编译后的 Java 代码,重点查看calculateDiscount方法的第 45 行:

public BigDecimal calculateDiscount(Order order) {

BigDecimal totalDiscount = BigDecimal.ZERO;

List<OrderItem> items = order.getItems();

for (OrderItem item : items) {

// 第45行:调用远程服务获取商品价格(正是之前定位的耗时方法)

BigDecimal productPrice = productRemoteService.getProductPrice(item.getProductId());

BigDecimal itemDiscount = productPrice.multiply(new BigDecimal("0.1")); // 10%折扣

totalDiscount = totalDiscount.add(itemDiscount);

item.setDiscountPrice(itemDiscount);

}

order.setTotalDiscount(totalDiscount);

return totalDiscount;

}

验证结果:生产环境代码与本地最新版本一致,排除版本不一致问题。

七、实战总结:破解卡顿诅咒的「终极方案」

通过 Arthas 诊断镜的 4 步咒文,已明确卡顿诅咒的根源:

  1. 问题定位:OrderCalculateServiceImpl的calculateDiscount方法调用productRemoteService.getProductPrice远程服务时,耗时长达 1170ms;

  1. 问题原因:产品服务响应缓慢(或网络延迟),导致订单计算流程卡顿;

  1. 解决方案

  • 短期:在calculateDiscount方法中添加缓存(如 Redis),缓存商品价格,避免频繁远程调用(如同战士携带备用物资,减少外出补给耗时);

  • 长期:优化产品服务的getProductPrice方法性能(如添加数据库索引、优化查询逻辑),或升级网络带宽,降低跨服务调用延迟。

八、Arthas 核心诊断咒文速查表

为方便各位代码战士在实战中快速调用,整理了常用的 Arthas 诊断咒文:

诊断场景

核心咒文(命令)

作用说明

定位 CPU 过高线程

thread -n 5

查看 CPU 占用率前 5 的线程,快速锁定异常线程

查看线程调用栈

thread 线程ID

查看指定线程的完整调用栈,定位卡顿代码行

追踪方法执行链路与耗时

trace 完整类名 方法名 -n 5

追踪方法的子方法执行耗时,定位拖慢性能的子方法

查看方法入参 / 返回值 / 异常

watch 完整类名 方法名 "{params,returnObj,throwExp}" -n 3

查看方法的输入参数、返回结果和异常信息,验证数据是否正常

反编译生产代码

jad 完整类名

反编译生产环境的 class 文件,验证代码版本是否为最新

查看 JVM 内存状态

dashboard

实时展示 JVM 内存、CPU、线程、GC 状态,全局监控 JVM 健康度

查看方法调用次数与耗时

monitor 完整类名 方法名 -c 5

每 5 秒统计一次方法的调用次数、成功次数、失败次数、平均耗时,监控方法性能

结语:成为生产环境的「故障破解师」

Arthas 诊断镜是 Java 战士在生产环境战场中的「必备法器」—— 它能帮你快速定位卡顿、死锁、内存泄漏等疑难问题,避免盲目重启服务导致的业务中断。记住:诊断问题的速度,决定了解决问题的效率;而掌握 Arthas 的核心咒文,能让你在 3 分钟内破解 90% 的生产环境性能问题!

若你在使用 Arthas 时遇到咒语失效(命令报错),或无法定位问题,欢迎在「跨域通信阵」(留言板)留下你的疑问,吾将为你解惑~