ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

【JVM原理详解】53-Arthas线上诊断实战

2026/8/15 2:22:39 拓冰建站 浏览量
【JVM原理详解】53-Arthas线上诊断实战 Arthas 线上诊断实战引言JDK 内置工具jstack、jmap能解决看什么的问题但很多时候我们需要看代码怎么跑的——某个接口为什么慢、某段逻辑走了哪个分支、方法参数和返回值是什么。传统方案只能加日志、重新发布在问题难以复现的线上环境几乎不可行。Arthas阿尔萨斯是阿里开源的 Java 线上诊断工具通过字节码增强技术在不重启应用的前提下实现方法级观测、动态修改代码、火焰图分析等能力。它已成为国内 Java 开发者排查线上问题的标配工具。本篇将系统讲解 Arthas 的安装、核心命令及完整实战排查流程。Arthas 安装与启动快速安装# 方式一下载 arthas-boot.jar推荐curl-Ohttps://arthas.aliyun.com/arthas-boot.jar# 方式二使用官方安装脚本curl-Lhttps://arthas.aliyun.com/install.sh|sh启动与 attachjava-jararthas-boot.jar启动后会列出当前机器上所有 Java 进程输入序号选择目标进程* [1]: 12345 org.example.MainApp [2]: 23456 com.example.Application Please input your choice:也可以直接指定 PIDjava-jararthas-boot.jar12345工作原理Arthas 启动后会通过Attach API连接目标 JVM然后利用Instrumentation API动态修改已加载类的字节码织入观测逻辑。这种机制决定了无需重启应用字节码在运行时被增强有轻微性能开销被观测的方法会有额外执行逻辑但未观测的方法不受影响退出后自动恢复Arthas 退出时会清除增强的字节码应用回到原始状态核心命令详解dashboard实时概览面板[arthas12345]$ dashboarddashboard 以全屏方式展示 JVM 的实时状态每隔几秒刷新一次内容分四块┌─ Thread ────────────────────────────────────────────────────────────────────┐ │ ID NAME GROUP PRI STATE %CPU DELTA TIME │ │ 1 main main 5 RUNNABLE 0.0% 0.00 10.2s │ │ 42 http-nio-8080-exec-1 main 5 WAITING 0.0% 0.00 2.1s │ │ 43 http-nio-8080-exec-2 main 5 RUNNABLE 12.5% 0.12 5.3s │ └─────────────────────────────────────────────────────────────────────────────┘ ┌─ Memory ────────────────────────────────────────────────────────────────────┐ │ heap used: 1.2G total: 2.0G max: 2.0G GC: G1 │ │ g1_eden used: 80M total: 200M │ │ g1_old used: 1.1G total: 1.5G │ │ nonheap used: 120M total: 256M │ └─────────────────────────────────────────────────────────────────────────────┘ ┌─ GC ────────────────────────────────────────────────────────────────────────┐ │ gc(count) gc(time) avg │ │ g1.young 15 0.234s 15ms │ │ g1.old 3 0.567s 189ms │ └─────────────────────────────────────────────────────────────────────────────┘ ┌─ Runtime ───────────────────────────────────────────────────────────────────┐ │ os.name Linux │ │ java.version 11.0.20 │ │ JVM start time 2026-07-17 10:00:00 │ └─────────────────────────────────────────────────────────────────────────────┘dashboard 适合问题初步定位——一眼看到 CPU 最高的线程、老年代使用率、GC 频率。thread线程分析# 查看所有线程thread# 查看 CPU 占比 TOP 5 的线程thread-n5# 查看指定线程thread43# 查看阻塞线程thread--stateBLOCKED# 查看死锁thread-bthread -n 5输出示例threads Total: 42, NEW: 0, RUNNABLE: 5, BLOCKED: 0, WAITING: 30, TIMED_WAITING: 7 Thread-43 Id43 cpuUsage12.5% deltaTime0.12s time5.3s RUNNABLE at com.example.service.OrderService.process(OrderService.java:120) at com.example.controller.OrderController.create(OrderController.java:45) ...thread -b检测阻塞其他线程的线程——即持有锁导致他人等待的线程对锁竞争排查非常有用Thread-43 Id43 BLOCKED on java.lang.Object7b0a1a28 owned by Thread-12 Id12jad反编译类# 反编译指定类jad com.example.service.OrderService# 反编译指定方法jad com.example.service.OrderService processjad 用于确认线上运行的代码版本是否与预期一致。常见场景发了版但怀疑代码没更新、怀疑类被其他 Agent 增强、排查 AOP 织入是否生效。输出会显示类加载器和反编译后的源码ClassLoader: -sun.misc.Launcher$AppClassLoader18b4aac2 public class OrderService { public Order process(String orderId) { // 反编译的代码 } }watch方法执行数据观测watch 是 Arthas 最常用的命令之一用于观察方法的参数、返回值、异常、执行耗时。# 基本语法watch类全限定名方法名观察表达式# 观察方法参数和返回值watchcom.example.service.OrderService process{params, returnObj}# 观察参数 返回值 耗时watchcom.example.service.OrderService process{params, returnObj, #cost}# 条件过滤只看耗时超过 200ms 的调用watchcom.example.service.OrderService process{params, returnObj}#cost 200# 只看抛异常的调用watchcom.example.service.OrderService process{params, throwExp}-x2输出示例methodcom.example.service.OrderService.process locationAtExit ts2026-07-17 11:00:00; [cost350.2ms] resultArrayList[ Object[][ # params String[ORD123456], ], Order[ # returnObj idString[ORD123456], amountBigDecimal[99.50], ], ]关键参数说明{params, returnObj, #cost}观察表达式用 OGNL 语法访问参数、返回值、耗时#cost方法执行耗时毫秒-x 2展开层级控制输出深度默认 1建议设 2~3location观测点支持AtEnter进入、AtExit正常返回、AtExceptionExit异常返回trace方法调用链耗时trace 用于追踪方法内部所有子调用的耗时帮助定位方法中哪一步最慢。# 追踪方法调用链trace com.example.service.OrderService process# 只看耗时超过 100ms 的调用trace com.example.service.OrderService process#cost 100# 追踪多次调用trace com.example.service.OrderService process-n5输出示例---[350.2ms] com.example.service.OrderService:process() ---[0.5ms] com.example.util.Validator:check() #35 ---[280.1ms] com.example.repo.OrderRepo:query() #36 ---[65.3ms] com.example.service.PricingService:calc() #38 ---[3.2ms] com.example.service.OrderService:save() #42 ---[0.8ms] com.example.service.NotificationService:notify() #45一眼看出OrderRepo.query()耗时 280ms是性能瓶颈。trace 默认追踪两层调用可通过--skipJDKMethod false显示 JDK 方法调用。stack查看方法调用栈stack 用于查看谁调用了某方法输出调用栈。# 查看 process 方法被谁调用stack com.example.service.OrderService process# 条件过滤stack com.example.service.OrderService processparams[0].length 10输出示例ts2026-07-17 11:00:00; thread_namehttp-nio-8080-exec-1; id43; is_daemonfalse; priority5 com.example.controller.OrderController.create() at com.example.controller.OrderController.create(OrderController.java:45) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at org.springframework.web.method.support.InvocableHandlerMethod.invoke(...) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1067) ...stack 适合排查某方法被意外调用的问题——比如某接口被大量调用但不知道来源。profiler火焰图Arthas 集成了async-profiler可生成火焰图用于 CPU 性能分析。# 启动 profiler默认采样 CPUprofiler start# 采样 30 秒后停止profiler stop--formathtml--file/tmp/flame.html# 生成 svg 火焰图profiler stop--formatsvg--file/tmp/flame.svg# 查看支持的事件类型profiler list生成火焰图后用浏览器打开横向是调用栈宽度代表 CPU 占比最宽的火焰就是热点。async-profiler 基于perf_eventsLinux或JFRJDK 11相比传统的采样 profiler 更准确且不会出现Safepoint Bias只在安全点采样导致的偏差。redefine/retransform热更新Arthas 支持运行时替换已加载类的字节码用于紧急修复线上 Bug。方式一redefine# 1. 反编译当前类jad com.example.service.OrderService --source-only/tmp/OrderService.java# 2. 修改代码编辑 /tmp/OrderService.java# 3. 编译为 class可用 mc 命令mc/tmp/OrderService.java-d/tmp/# 4. redefine 替换redefine /tmp/com/example/service/OrderService.class方式二retransform# retransform 使用 Arthas 字节码增强后的 classretransform /tmp/OrderService.class# 查看历史 retransform 记录retransform-l风险提示redefine 是直接替换字节码Arthas 退出后不会自动回滚与 watch/trace 不同不能修改方法签名、字段定义、类继承关系只能修改方法体生产环境慎用建议仅用于紧急止血修复后仍需正常发布版本实战线上接口耗时排查完整流程问题背景某电商系统下单接口/api/order/createP99 耗时从 200ms 飙升到 2s日志无明显异常GC 正常。需要在不重启服务的前提下定位瓶颈。第一步全局概览# attach 到目标进程java-jararthas-boot.jar12345# 查看概览dashboard发现 CPU 和内存正常但http-nio-8080-exec-*线程频繁出现状态多为 RUNNABLE。第二步定位耗时线程# CPU TOP 5 线程thread-n5发现http-nio-8080-exec-3CPU 占比最高栈在OrderService.create方法。第三步追踪方法调用链# 追踪 create 方法的内部调用耗时trace com.example.service.OrderService create-n3---[1850.2ms] com.example.service.OrderService:create() ---[0.5ms] Validator:check() ---[1200.3ms] OrderRepo:queryOrder() # 慢 ---[620.1ms] PricingService:calcPrice() # 慢 ---[25.0ms] OrderService:save()发现OrderRepo.queryOrder()耗时 1.2sPricingService.calcPrice()耗时 620ms。第四步深入观测慢调用# 观察 queryOrder 的参数和返回值watchcom.example.repo.OrderRepo queryOrder{params, returnObj, #cost}#cost 500-x2methodcom.example.repo.OrderRepo.queryOrder locationAtExit ts2026-07-17 11:05:00; [cost1150.3ms] resultArrayList[ Object[][ # params String[ORD_20260717_001], ], Order[ # returnObj idString[ORD_20260717_001], itemsArrayList[isEmptyfalse;size1500], # 1500 个商品 ], ]发现返回了 1500 个商品的订单——这是个异常大的订单。第五步定位 PricingService 慢的原因# 追踪 calcPrice 内部trace com.example.service.PricingService calcPrice-n3---[620.1ms] com.example.service.PricingService:calcPrice() ---[600.5ms] DiscountService:batchCalc() # 批量折扣计算 ---[15.2ms] TaxService:calc() ---[4.1ms] PricingService:aggregate()继续深入# 观察 batchCalc 参数watchcom.example.service.DiscountService batchCalc{params, #cost}-x1发现batchCalc接收了 1500 个商品做批量折扣计算内部有嵌套循环导致 O(n²) 复杂度。第六步确认根因# 查看调用来源stack com.example.service.DiscountService batchCalc确认是上游传入了超大批量订单。结合日志发现是某促销活动触发了合并下单逻辑。第七步火焰图辅助验证# 采样 60 秒生成火焰图profiler start# 等待 60 秒...profiler stop--formathtml--file/tmp/flame.html火焰图中DiscountService.batchCalc占比最高与 trace 结论一致。第八步紧急止血# 确认当前代码jad com.example.service.DiscountService batchCalc --source-only/tmp/DiscountService.java# 修改加入批量分片逻辑每 100 个一批# ... 编辑代码 ...# 编译mc/tmp/DiscountService.java-d/tmp/# 热更新redefine /tmp/com/example/service/DiscountService.class修复后 P99 恢复到 200ms 以内后续通过正常发布流程固化修复。实践要点使用注意事项性能开销watch/trace 会对目标方法织入逻辑高 QPS 接口上长时间开启可能影响性能。建议设置-n限制执行次数排查完立即stop。OGNL 表达式watch/trace/stack 的条件过滤使用 OGNL 语法。常用变量params参数数组、returnObj返回值、throwExp异常、#cost耗时、target当前对象。条件过滤避免噪音高频调用不加过滤会刷屏。用#cost 100、params[0].length 10等条件精准过滤。退出与清理stop命令会清除所有增强并退出 Arthas。切忌直接 kill 进程可能导致增强的类未恢复。正确退出方式# 先清除增强reset# 再退出stop安全与权限Arthas 能查看和修改运行时代码等同于拥有应用完全控制权生产环境务必限制使用人员。Arthas 通过 Unix Domain Socket 或 TCP 通信建议不在公网环境暴露。如需远程使用通过 SSH 跳板机登录服务器执行。redefine修改的代码不会持久化重启后会丢失。务必在热修复后通过正式发布固化修复。常用命令速查命令用途示例dashboard实时概览dashboardthread -n 5CPU TOP5thread -n 5thread -b阻塞线程thread -bjad反编译jad com.example.Servicewatch观测方法watch cls method {params,returnObj}trace调用链耗时trace cls method #cost 100stack调用栈来源stack cls methodprofiler火焰图profiler start; profiler stopsc查找类sc -d com.example.Servicesm查找方法sm com.example.Service methodmonitor方法统计monitor cls method -c 10logger动态日志logger --name ROOT --level DEBUGredefine热更新redefine /tmp/cls.class小结Arthas通过字节码增强实现运行时方法级观测是排查代码怎么跑问题的利器弥补了 JDK 工具无法深入方法内部的短板。trace watch 组合是排查耗时问题的标准流程trace 定位慢在哪一层watch 看具体的参数和返回值。profiler 火焰图提供 CPU 热点的全局视角适合复杂调用链的性能分析。redefine 热更新可用于紧急止血但有风险且不持久化生产环境慎用并需后续正式发布固化。安全第一Arthas 等同于应用 root 权限务必限制使用人员排查后用resetstop正确清理。下一篇将系统梳理常见 JVM 参数配置为生产环境的 JVM 调优提供参数级指南。