资讯动态

Arthas实战:Java线上问题排查与JVM诊断工具详解

发布时间:2026/10/5 3:21:30 来源:尧图企业网站定制
如果你写了一两年Java服务端大概率遇到过这种场景线上接口突然超时、CPU被打满、某个请求的入参莫名其妙但你翻遍日志、加遍日志、重启几次服务都找不到根因。改代码、发版本、等重启一个来回至少半小时期间用户在那一边一遍地刷页面。Arthas就是用来治这种线上问题等不起的工具它是阿里巴巴开源的Java在线诊断利器不需要改业务代码、不需要重启服务直接attach到正在运行的JVM上动态地看类加载情况、方法入参出参、线程状态、调用链路耗时甚至直接反编译线上class。这篇文章我会把我日常用得最多的几种方式和对应的排查场景串起来讲适合那些已经在用Spring Boot、对JVM有一定了解、但遇到线上疑难杂症主要靠猜和重启的Java开发。1. 为什么线上排查用Arthas而不是靠加日志要先说清楚Arthas到底解决的是什么问题不然很容易把它当成一个高级点的jstack。JVM自带的工具其实不少jstack可以看线程栈、jmap可以导堆、jstat能看GC但这些工具看的是静态快照而且对很多业务问题它们根本派不上用场。我经历过最典型的一次事故一个订单接口偶发性耗时超过10秒频率大概每天几十次日志里根本没有异常堆栈只有接口耗时打印。你加日志也很难因为你不知道是哪个方法慢、哪个参数触发的、什么条件下变慢。这种问题用jstack去抓线程栈抓十次有九次是正常的因为那几十次慢请求分散在一天里你不可能一直蹲着守。Arthas的核心价值在于它让你在线做两类事情观察和实验。观察是像watch、trace、thread这类命令直接在运行中的方法上埋点看到真实流量下的入参、返回值、异常和耗时分布。实验是像ognl、vmtool这样的命令能在线执行一段表达式、调用一个bean的方法、甚至修改某个静态字段的值不用重新发版。数据上来之后排查就变成了一道判断题而不是一道盲猜题。与其对着jstack猜哪个线程有问题不如先想清楚Arthas的几个核心机制用起来才不会心里没底。第一个机制是字节码增强。Arthas使用的是字节码Instrumentation技术原理是在类已经加载到JVM之后通过java.lang.instrument的retransform能力把方法体在字节码层面改掉插入统计、拦截逻辑。举个例子你watch一个方法Arthas不是通过反射或代理去调用它而是把这个方法的字节码改了在方法入口和出口加了钩子真实调用链在执行过程中就会经过这些钩子把参数和返回值带出来。这也是为什么watch能拿到真实的入参、出参而不是像debug一样断住线程。第二个机制是attach。Arthas启动时会attach到目标JVM这个动作是JDK自带能力所以它对运行中的服务影响很小通常只会增加微秒级别的耗时。attach成功后它会提供一个telnet端口和一个http端口你在本机或者服务器上敲命令本质上是往这个端口发指令。接下来在真正开始用之前有必要把安装启动这一步走顺很多人在第一步就被版本问题卡住了。2. 启动Arthas的完整流程和最容易卡住的几个细节Arthas的安装非常轻量一个jar包搞定下载地址GitHub上就有国内一般用阿里云镜像加速。它的启动方式有两种我分别说我的习惯和理由。方式一是直接下载arthas-boot.jar然后用java -jar启动curl -O https://arthas.aliyun.com/arthas-boot.jar java -jar arthas-boot.jar启动后它会列出当前机器上所有Java进程你输入序号选择要诊断的进程回车就连上去了。初次使用建议用这个方式因为里头自带下载补全依赖的逻辑会把配套的arthas-core、arthas-agent这些lib都拉下来省事。方式二是用as.sh脚本适合平时经常用、想要一条命令直连的场景curl -L https://arthas.aliyun.com/install.sh | sh ./as.sh pidas.sh脚本本质还是调arthas-boot.jar只是简化了找进程这一步直接把pid传进去就行。我自己的习惯是用方式一因为大多数时候我不是只诊断一个进程会经常切换。连接成功之后不急着敲命令先执行一下dashboard看看全局状态。dashboard显示的是整个JVM的概要信息包括CPU使用率、内存占用、GC情况、活跃线程和每个线程的CPU占比。连接不上或者命令无效是新手最容易踩的坑这里有几个点值得提前说。第一Arthas启动必须有JDK环境。很多生产机器的Java是JRE only或者说java命令能跑但tools.jar不在导致attach失败、ASM字节码转换报NoClassDefFoundError。现在的Arthas版本依赖的是JDK自带的com.sun.tools.attach如果你运行java -jar arthas-boot.jar时报Can not find java agent或者Well, this is unexpected先检查JAVA_HOME是否指向完整的JDK目录而不是jre目录。第二注意权限问题。如果你用Tomcat或容器启动服务进程可能属于别的系统账号当前账号attach会失败。这时候需要sudo运行arthas或者切到同账号。我遇到过好几次sudo倒是成功了但telnet端口默认用的3658被防火墙挡了导致外部机器连不上、本机telnet也失败排查半天结果是被安全组拦了。如果和自己机器不在同一内网可以把端口映射出来或者用Arthas自带的WebSocket方式。第三一个服务只能attach一个Arthas实例吗不是Arthas支持多会话不同终端可以各attach各的但同一个进程同时开太多会有字节码重复增强的问题尤其是对同一个类重复enhance。我一般一个诊断窗口互相切换而不是开四五个终端一起往同一进程上怼。连接成功且dashboard看起来正常之后才算真正进入正题。3. 我最常用的5个命令和对应的实战排查场景用了一段时间Arthas真正高频翻牌子的其实就那么几个。我先列个总表把命令、用途、使用频率对应起来然后逐个讲怎么用。命令用途使用频率dashboard全局JVM状态高每次必先看thread线程栈与线程CPU高定位卡顿和死锁watch观察方法入参、返回、异常极高业务问题主力trace方法内部调用链耗时极高性能定位主力jad反编译线上class中确认线上代码版本sc / sm查询类和方法信息中辅助定位3.1 dashboard先看整体再动手dashboard的输出信息量很大但你要抓住重点。它最上面是内存和GC区域Old区的占用趋势、Full GC次数和耗时如果异常通常说明堆有问题这时候该去导出dump而不是继续用Arthas。中间是线程列表按CPU占用排序瞬间就能发现哪个线程在偷吃CPU。我举一个实际看过的输出片段做说明字段含义比具体数值更重要ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTED DAEMON 472 nioEventLoopGroup-3-1 main 5 RUNNABLE 12.5 0:27 false trueNAME字段里的关键词很有用比如出现大量的http-nio-exec或者tomcat-thread说明是Web容器线程出现gc线程或者JIT编译线程就该怀疑是JVM自身行为导致CPU升高如果看到业务自定义线程池名字比如order-async-thread-56那基本就是业务代码的问题区间。3.2 thread搞清楚线程到底在干什么thread命令最常用的组合是直接看CPU最高的前几个线程栈以及查找死锁。# 查看CPU占用前3的线程n参数指定数量 thread -n 3这个命令会打印对应线程名的线程栈能看到栈顶方法。比如tomcat线程栈顶停在某个JDBC驱动调用上说明可能是数据库连接获取慢停在某个锁等待的park方法上说明是被别的线程卡住了。再配一把thread -b查看阻塞线程可以把持有锁的那个线程也找出来线上死锁基本当场破案。有一个高频误用需要注意thread命令看到的栈是当前时刻的瞬时快照而偶发性问题的特征是不稳定所以单抓一次往往看不出名堂。我的做法是配合trace或watch先锁定时间段再在这个时间段内用thread -n抓现场而不是一上来就乱抓。3.3 watch最快锁定异常参数的入口watch是业务问题排查里含金量最高的命令它可以观察一个方法的调用实参、返回结果和异常信息。最基本的形式watch com.example.OrderService createOrder params[0] returnObj意思是观测OrderService类的createOrder方法打印第一个参数和返回值。如果你关心整个入参对象可以这样写watch com.example.OrderService createOrder {params, returnObj, throwExp}花括号表达式会把入参、返回值、异常一起打出来。如果只想观测满足某个条件的情况可以用OGNL条件过滤比如只看订单金额大于10000的慢请求watch com.example.OrderService createOrder {params[0], returnObj} params[0].amount 10000这个过滤对于生产环境非常重要否则线上流量一大watch的打印量会瞬间把终端刷爆Arthas本身的开销倒是不大但刷屏会妨碍分析。3.4 trace一眼看穿方法内部的耗时分布如果说watch回答的是传入什么、返回什么trace回答的就是时间都花在哪了。执行trace com.example.OrderService createOrder只要createOrder被调用一次终端就会打印出这个方法内部的调用树和每段耗时的分布。最典型的输出是---[10ms] com.example.OrderService.createOrder ---[6ms] com.example.PriceService.calcPrice ---[5ms] com.example.PromoService.applyCoupon一眼就能看出来价格计算慢主要是优惠券这个方法耗了5毫秒而不是订单主逻辑慢。这比靠猜精准太多。注意点trace的耗时不包含被观察方法自身反序列化参数、序列化返回值的时间所以精确性足够用于定位但不要太纠结小数点后面的偏差。另外trace对高频方法会放大打印量建议用-m跳过公共方法、用条件表达式限定采样范围比如trace com.example.OrderService createOrder #cost 100只在耗时超过100毫秒时才打印调用树这对偶发慢请求特别有效。3.5 jad sc sm确认线上代码到底是哪一版还有一种很常见的线上问题本地代码和线上不一致或者你怀疑线上跑的jar包根本不是最新构建的那版。用jad直接反编译已经加载的类jad com.example.OrderService出来的源码就是线上实际执行的字节码编译后的反汇编结果和本地一对比就能看出差异。scsearch class和smsearch method是辅助定位用的sc -d com.example.OrderService可以列出类加载器、类路径sm com.example.OrderService可以列出所有方法签名方便你确认watch或trace时方法名写没写对。这个组合拳在生产上有一个极其实用的场景你怀疑框架自动生成的代理或动态代理类把业务逻辑搞坏了。sc一查看到的是cglib代理类还是JDK动态代理jad看去掉代理后的真正实现类分分钟确定问题出在处理链的哪一环。4. ognl、tt和火焰图从能看到能动上面的命令是用来看的但如果问题需要验证某个假设或者需要绕过代码直接在线做实验就得用ognl和tt了。4.1 ognl在线执行一段表达式ognl可以理解为在JVM里执行一个表达式类似在代码里写一句调试代码看效果。前提是能拿到对象方式有两种通过静态方法入口或者通过Spring Context拿到bean。常见用法一看静态字段的值ognl com.example.ConfigMAX_RETRY常见用法二调用某个Spring bean的方法ognl #contextorg.springframework.web.context.ContextLoadergetCurrentWebApplicationContext(), #bean(userService), #bean.getUserCount()这里要特别说明fetch对象的方式不止一种Spring Boot里更通用的做法是通过ApplicationContext的静态持有者拿bean不同项目的持有者可能不同有的项目是SpringContextHolder有的是AppContext要根据代码里实际的持有类来写。ognl不是万能的它不能执行复杂的for循环、创建复杂对象只能做单行表达式的活。如果真的需要更复杂的在线逻辑我一般会用vmtool配合invoke或者干脆临时写一个Java方法用Arthas加载进去但那种需求很少碰到。还有一类场景是配合watch用OGNL表达式过滤效果等同于把线上流量里面符合特征的那部分单独筛出来。4.2 tt时间隧道记录调用现场tt的定位非常独特它可以把你关心的某一次方法调用完整记录下来之后根据记录重放或者直接查看当时的调用上下文。核心的用法是# 记录OrderService.createOrder方法的最近100次调用 tt -t com.example.OrderService createOrder -n 100执行之后会返回一个时间列表每行都有一个INDEX编号。然后你想看其中某一次的入参、返回值、异常和调用环境用tt -i 1003甚至可以重放这次调用tt -p 1003重放意味着在不写任何测试代码的情况下用线上真实参数把那个方法再执行一遍适合复现那些只在特定参数下出现的问题。4.3 火焰图把CPU热点可视化前面thread -n能看到某一个瞬间哪个线程吃CPU但CPU问题往往是看起来不忙但整体很高或者某个方法的开销平时看不出来。Arthas集成了一个性能分析利器async-profiler生成火焰图的入口是profiler命令。执行步骤很简单# 开始采样默认采样的是CPU profiler start # 过一段时间比如30秒后停止并生成html文件 profiler stop --format html生成的html文件会放在/tmp目录下可以直接下载到本地用浏览器打开。火焰图怎么看横轴是采样次数等价于CPU时间占比越宽的栈代表耗时越多纵轴是调用栈嵌套关系从下往上是Java方法-框架代码-自己的业务代码。重点看顶部和中部那些又宽又长的栈那才是热点的真正所在。我实际用它定位过一个问题服务CPU平时只有30%上线新功能后升到80%zzz用thread -n死活看不出线程栈都集中在哪个方法火焰图一出来立马看到有一个新加的JSON序列化方法占了近40%的宽度那方法每次请求都在循环里被调用属于明显的重复序列化浪费。改成复用ObjectMapper后CPU直接降回35%。注意profiler在高并发和极端负载下会有一定性能开销建议在流量相对平缓时采样避免因为采样工具本身加重服务负担。4.4 把几个命令串成一个排查流程诊断是个流程命令是零件拿一个真实的CPU飙升场景完整走一遍就明白了。第一步输入dashboard看CPU占用和活跃线程如果GC不高排除JVM自身原因。第二步输入thread -n 5拿到当前CPU占用最高的线程栈发现栈顶是一个自研的订单价格计算模块。第三步输入trace锁定具体耗时占比最高的方法。第四步如果还找不到用profiler采样30秒看更宏观的火焰图再定向优化。反过来如果问题是偶发性接口超时就把第一步换成watch加条件表达式过滤慢请求第二步用trace确认耗时分布第三步用tt记录那几次有问题调用的入参第四步用ognl或本地测试代码复现最后定位根因。5. 生产环境使用Arthas的避坑手册Arthas很好用但直接用在生产上有一些细节需要重视。以下全是我自己踩过或者看同事踩过的坑逐条讲清楚。5.1 对热门类做增强会影响什么watch或trace的字节码增强本质是改方法体如果这个方法是超高QPS的公共方法比如某个基础组件的关键路径那每次调用都会多走一段代理逻辑性能影响虽小但并非为零。实测过一个简单方法在加了watch之后单次调用大概多0.5到1微秒正常情况下无感。但如果是事务性核心链路上本来就1毫秒的方法默默多了0.5微秒不多但几百万次调用累积下来也不是完全可以忽略的。所以我的原则是能缩小范围就缩小范围能用条件表达式限制就限制诊断完立刻stop或退出会话别挂着不关。5.2 防火墙、端口和版本兼容Arthas默认的telnet端口是3658http端口是8563。如果你需要从开发机连到生产服务器确保这两个端口在安全组或者iptables里放通。有时候你用启动脚本连上了、dashboard也显示了但本地idea控制台连不上十有八九是端口没放通。版本兼容方面Arthas对JDK8和JDK11支持得最好JDK17的某些版本需要较新的Arthas版本早期的版本对虚拟线程JDK21的VirtualThread支持不够好。遇到诊断不到虚拟线程的情况先把Arthas升级到最新版再排查。5.3 退出和卸载结束诊断时直接CtrlC退出终端不一定能完全卸载agent。Arthas提供了一个停止命令stop停止之后字节码增强会被还原方法会恢复到原始状态不会再产生任何额外开销。对于那种诊断完就不管的做法长时间挂着会让进程持有额外的线程和端口虽然没有大问题但这种事做多了迟早会在某个诡异问题上多花一个小时的排查时间。5.4 日志和敏感信息风险watch、ognl这类命令会把入参、返回值直接打印在终端如果你的接口入参里有身份证号、手机号、token这类敏感数据打印出来再顺手贴到截图群聊里问题就大了。我的习惯是必要情况下先把接口日志脱敏或者尽量用条件过滤只打印满足条件的那几条记录在诊断完成后即时清屏或退出不要把敏感数据留在终端历史里。5.5 和链路追踪、日志平台配合使用Arthas不是日志系统的替代品它是日志系统的补充。如果你所在的团队有SkyWalking、Jaeger这类APM工具优先用APM做粗筛定位把嫌疑收敛到具体方法和时间段再用Arthas做细粒度观察验证。两个工具各干各的定位粒度结合起来才是最高效的排查组合。6. 从常用命令到调试思维Arthas帮我养成的诊断习惯工具用熟了之后真正改变工作方式的不只是命令本身而是一套在线验证的调试思维。以前碰到线下环境复现不出来的问题会让你陷入反复加日志、发版、看日志、再改代码的循环里一次循环至少十分钟几次下来时间全耗在构建和部署流水线上。现在碰到线上问题我的第一反应是这个问题能不能在不重启的情况下用Arthas先把现场数据拿全。不能再考虑发版加日志。拿到现场数据之后能定位到方法级甚至行级再决定改哪里。这套流程最大的好处是把猜测--验证的周期从分钟级降到秒级。具体到日常习惯我总结了三句话。第一句先dashboard看全局再thread找线程最后trace/watch深入方法。第二句尽量用条件表达式缩小观察范围让数据说话而不是让刷屏说话。第三句诊断完立刻stop别让线上环境留着一个挂着不走的agent。我经手过的大多数线上疑难问题复盘到最后都发现不是问题本身有多难而是现场数据没拿全就开始猜了。Arthas这类工具解决的就是拿现场数据这个环节。它可以让你站到正在运行的代码面前问它一句你刚才到底干了什么。如果你还没用过Arthas遇到下一个线上诡异问题的时候就是最好的入门机会。按照这篇文章的顺序先dashboard看看全局再用thread和watch去追踪一个具体问题最后用火焰图把热点拉出来走完一遍你就不会再回到盲猜加日志的老路上了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑