拓冰建站拓冰建站
首页 / 资讯中心 / 正文

Arthas进阶实战:线上JVM问题排查与命令详解

中午刚处理完一个线上接口超时的问题用Arthas定位到是某个第三方调用在等待锁全程没重启服务、没加一行日志十分钟内从怀疑到确认。如果你还在靠反复加日志、重启复现来排查线上问题那我强烈建议你认真看看这篇文章。Arthas是阿里巴巴开源的Java诊断工具能在不修改代码、不重启应用的前提下对运行中的Java进程做实时排查和动态干预。它解决的正是Java开发最头疼的线上问题无法本地复现、日志不完整、临时加日志要重新发版。这篇文章适合已经被Arthas基础命令打动过、但还想用得更加深入的人也适合那些装好了Arthas却因为启动就报错而搁置的人。我会把启动疑难、URL调用路径追踪、命令的底层原理和实战场景串起来一次性讲透进阶用法。2. 为什么线上问题必须用Arthas这类工具解决2.1 线上诊断的“最后三分钟”困境说句实在话大部分Java应用的问题不是查不到而是来不及查。一个接口偶发超时本地压测压不出来一个内存缓慢增长日志里除了OOM前的一点痕迹什么都看不到一个死锁现场一重启就没了。传统的排查手段是怀疑哪个方法有问题就在哪个方法里加日志重新打包、发布、等待问题复现、收集日志、分析、再改。这一套循环下来轻则几小时重则几天而且很多线上故障根本等不起这个周期。Arthas的价值在于它把“诊断”这个动作从开发态搬到了运行态。你不需要预埋任何探针不需要提前在代码里写日志而是等问题发生的那一刻像外科手术一样精准地伸进JVM内部看线程栈、看方法入参返回值、看调用耗时、甚至直接修改字节码。这等于给线上系统装了一个随时可用的显微镜。2.2 Arthas和JDK自带工具的定位差异JDK自带的那套工具jstack、jmap、jstat、jcmd解决的是“系统级指标”问题线程数多少、堆占用多少、GC频率如何。但它们的短板同样明显你能看到线程状态却看不到这个线程正在执行的业务方法参数你能看到堆内存占用却不知道是哪段代码创建的这些对象。Arthas的能力恰恰是“代码级”的。它基于Java的Instrumentation机制在类加载之后动态织入增强逻辑让你能在任意方法上做“临时埋点”。这种能力在原生JDK工具里是没有直接对应的。举一个直观的例子jstack能告诉你线程Blocked在某个锁上但不会告诉你这个锁是被谁持有的、持有了多久、业务参数是什么。而Arthas的thread命令可以显示锁对象信息配合watch命令盯住锁资源整个问题链条就完整了。所以我的建议是jstack/jmap这类工具用于“粗筛”Arthas用于“精确定位”。两者配合效率最高。如果你还在用笨办法加日志排查强烈建议把Arthas纳入你的常备工具箱它解决的是“最后三分钟”的临门一脚问题。3. 核心命令的底层逻辑与实战拆解3.1 命令不是背出来的先理解Arthas的命令模型接触过Arthas的同学应该知道它启动后进入一个交互式命令行输入命令回车执行。很多人觉得命令难记其实是不理解Arthas的命令模型。所有的Arthas命令本质上做两件事要么是查询JVM运行信息要么是在类的方法上做字节码增强。增强类命令watch、trace、tt、monitor、stack有统一的参数模式命令名 类名 方法名 [条件表达式] [监听选项]类名和方法名用来定位“在哪里增强”条件表达式用来过滤“什么时候触发”监听选项控制“增强后做什么”。理解了这个模型你遇到一个没见过的命令也能大概推断出它需要哪些参数。举个例子watch com.example.OrderService createOrder {params, throwExp} -x 2这条命令拆开看就是在OrderService类的createOrder方法上做监听当方法被调用时打印入参和异常信息。这是Arthas最核心的思维模式。3.2 高频命令的细节用法与适用场景dashboard是入门命令显示整体大盘线程、内存、GC、运行时信息。它的进阶用法是可以加-i参数调整刷新间隔毫秒。排查内存问题时我会把dashboard开着同时触发业务操作观察堆内存曲线的变化节奏判断是否存在“每次都涨但不回落”的泄漏特征。thread命令是排查线程问题的利器。thread -n 3直接列出CPU占用最高的三个线程会带上线程栈。这是排查CPU飙高的首选命令。有个细节很多人不知道thread -b可以找出当前阻塞其他线程的锁也就是死锁问题的“罪魁祸首”。配合thread --state BLOCKED可以只看阻塞态的线程。watch是日常用得最多的命令它能在方法执行前后取到入参、返回值、异常。关键选项有三个-x控制展开深度-b在方法调用前执行此时拿不到返回值-e在异常抛出时触发。实战中我常用watch 类名 方法名 {params, returnObj, throwExp} -x 3来一次性拿到完整信息。trace命令是性能瓶颈分析神器。它能输出方法内部各子调用的耗时分布精确到每个被调方法。值得注意的是trace默认只追踪一层调用关系想看更深的调用链需要通过-n或多次trace嵌套来完成或者使用--skipJDKMethod false把JDK内部方法也纳入统计。排查慢接口时trace的输出能直接告诉你时间花在哪个子调用上。tt命令是“时间隧道”它能记录方法调用现场之后可以回放。对于那种“偶发异常、过了就没了”的问题tt是救命级的工具。tt -t 类名 方法名开始记录tt -i 索引查看某次调用的完整参数和返回值tt -p 索引甚至可以重放一次调用。3.3 ognl表达式Arthas里最被低估的能力Arthas的命令参数大量使用ognl表达式它就是在运行时对对象图做导航和计算的表达式语言。你可以把它理解为“在JVM里执行的、可以访问到当前实例和上下文的Java代码片段”。比如你想查看一个Spring Bean的某个属性ognl -x 3 #beancom.example.ApplicationContextUtilgetBean(userService), #bean.userCache.size()这条命令调用了静态方法获取Bean然后读取了它内部的一个缓存对象的大小。这种能力在排查“缓存什么时候被清空了”“配置到底生效没有”之类的问题时极其实用。ognl表达式的学习门槛在于熟悉上下文变量。在watch命令里params、returnObj、throwExp、target是固定的四个上下文对象在ognl命令里你可以用类名静态方法调用静态方法用#变量名表达式, 表达式做多步操作。花一两个小时把ognl练熟Arthas的战斗力会提升一大截。4. 启动疑难实录Arthas启动时拿不到jps进程怎么办4.1 为什么Arthas找不到目标Java进程很多人在第一次接触Arthas时卡住的不是命令不会用而是压根启动不了。最常见的一种报错场景是执行java -jar arthas-boot.jar之后Arthas列出进程列表但你想诊断的那个Java进程根本不在列表里或者提示“Can not find java process”。这里要先明白Arthas是如何发现进程的。Arthas通过JDK的jps工具Java Virtual Machine Process Status Tool来枚举本机Java进程。jps的原理是扫描临时目录下的hsperfdata_用户名目录每个运行中的JVM会在这个目录下生成一个以PID命名的文件。如果这个文件不存在、读不到、或者目录位置不对jps就发现不了对应的进程。所以Arthas找不到进程绝大多数情况是jps本身就看不到那个进程。理解了这一层排查思路就清晰了。4.2 jps失效的几类典型场景**场景一进程归属用户不一致。**如果你用root用户执行Arthas但Java进程是tomcat用户启动的那么Arthas去扫描root用户目录下的hsperfdata自然找不到tomcat用户启动的进程。解决方案是切到与目标进程相同的用户来执行Arthas。场景二临时目录被清理。/tmp目录下的hsperfdata文件可能被系统的定时清理任务删除或者被手动清理。JVM虽然还活着但它那个性能数据文件已经没了jps自然就看不到它。这种时候重启应用是最后的办法但在重启之前还有更轻量的方案可以抢救一下。**场景三容器环境隔离问题。**在Docker或K8s里Arthas跑在宿主机上目标Java进程跑在容器里两边看到的PID命名空间不同临时目录也隔离jps基本不可能跨容器发现进程。**场景四JVM参数显式关闭了PerfData。**如果启动参数里有-XX:-UsePerfDataJVM不会生成hsperfdata文件jps永远发现不了这个进程。4.3 绕过jps手工指定进程的三种方法既然jps不可靠那就有必要绕开它。Arthas提供了--pid参数可以直接指定目标进程PID绕开进程枚举环节java -jar arthas-boot.jar --pid 12345这个方式的前提是你知道目标进程的PID。如何获取在容器场景里PID不是关键问题关键是要在同一个PID命名空间下执行。所以最稳妥的方式是把arthas-boot.jar复制到容器内在容器内部执行。如果是K8s环境可以用kubectl exec进入容器再操作。还有第二种方式利用Java的Attach机制主动连接。Arthas支持通过attach参数直接连接思路是让Arthas自己去找JVM并attach上去不需要jps先枚举出来。你可以在启动Arthas之前先用ps -ef | grep java找到完整启动命令确认进程确实存在且健康状态再用--pid方式连接。第三种方式是修改Arthas查找进程的策略。如果你用的是较新版本的Arthas可以考虑设置环境变量ARTHAS_IGNORE_JPS1让Arthas改用其他方式枚举进程。这个选项在不同版本里支持程度不同建议先查一下对应版本的文档。如果这些方式都走不通唯一剩下的办法是重启应用并在重启后第一时间用--pid方式连接。这个顺序一定要记清楚先确认进程存在再尝试各种连接方式最后才考虑重启。4.4 实战教训容器内启动Arthas的避坑清单容器环境里踩过的坑最多我总结了几条经验**必须使用与目标进程相同的用户执行Arthas。**很多容器镜像以非root用户运行应用如果Arthas以root启动attach时可能因为权限问题失败。**确保容器内存在java命令。**Arthas运行阶段需要java环境有些精简镜像只有JRE没有JDK。虽然Arthas附带了大部分需要的类但缺少关键工具时还是会报错。解决方法是使用带JDK的镜像或者把arthas-boot.jar放在宿主机、用--target-ip方式远程attach。**小心PID命名空间。**在容器内执行ps -ef看到的PID和宿主机看到的不一样。在容器内使用--pid时必须使用容器内的PID而不是宿主机的PID。**内存受限的容器注意Arthas自身开销。**Arthas启动时会加载不少类占用一些内存。在内存吃得非常紧的容器里建议把Arthas的堆内存限制下来可以通过JAVA_OPTS传入-Xmx256m之类的参数避免因为Arthas启动导致容器OOM。如果一个进程连jps都发现不了但它明明还活着先用ls -l /tmp/hsperfdata_用户/确认一下目录内容如果文件不在那么任何来自jps枚举的方式都是白费功夫。直接跳到--pid方式这是最可靠的路径。5. URL调用路径追踪实战能不能跟、怎么跟5.1 trace命令跟踪HTTP请求的先天局限有一个高频问题Arthas能不能直接跟踪一个URL的完整调用路径很多人的预期是我输入一个URLArthas就自动显示出这个请求从Controller到Service到DAO的全链路耗时。但Arthas本身是不支持“URL维度的自动链路追踪”的。原因很简单URL只是HTTP协议层面的概念进入JVM之后它变成了一次方法调用链。Arthas做的是代码层面的字节码增强它不知道也不关心“这次调用是由哪个URL发起的”。它只知道“这个方法被调用了参数是什么耗时多少”。这和APM工具如SkyWalking、Pinpoint的工作机制有本质区别后者会通过字节码增强自动维护一个TraceId把所有跨方法、跨线程的调用串联起来形成一条完整的调用链视图。Arthas的思路不一样它更像一个“定点狙击”工具需要你告诉它看哪个方法它才能告诉你那个方法发生了什么。5.2 从入口到出口trace/watch组合还原完整调用链但Arthas确实可以做到“跟踪URL调用路径”只是你需要手工多走几步。核心思路是从入口方法开始一层一层向下追踪。首先找到HTTP入口。Spring MVC应用入口自然是Controller层的方法。先用trace命令跟踪Controller方法找出耗时最长的子调用trace com.example.controller.OrderController createOrder执行这个命令后你发起一次HTTP请求Arthas会输出OrderController.createOrder方法内部所有子调用的耗时情况。你会发现耗时集中在了某一个Service方法上。然后针对这个Service方法继续tracetrace com.example.service.OrderServiceImpl createOrder如此逐层深入直到你定位到具体的瓶颈点。这种方式虽然需要人工跟踪几步但好处是极其灵活你想从哪一层切入都行没有APM那种固定框架的束缚。如果你怀疑某个特定URL对应的入口方法不确定可以先使用stack命令试探性地看调用栈stack com.example.web.CommonInterceptor preHandle很多Spring Boot应用都有拦截器preHandle会被所有请求触发。用stack命令在这个方法上监听当请求进入时Arthas会打印完整的调用栈包含是哪个Controller方法在处理。这样你就拿到了URL到入口方法的映射关系。5.3 tt命令在URL跟踪中的妙用tt命令在处理“偶发问题”时有独特价值。场景是这样的某个接口每天凌晨偶发报错你看不到规律也抓不到现场。这时你可以在可疑方法上挂上tt记录tt -t com.example.service.OrderServiceImpl createOrder然后等待调用发生。每次调用都会被记录产生一个索引号。等调用结束你可以通过tt -i 索引号查看那一次调用的完整入参、返回值、异常堆栈。这就相当于给所有调用拍了快照事后任意回放查看。tt的进阶用法是结合条件表达式做精准采集。比如你只关注某个特定用户的请求tt -t com.example.service.OrderServiceImpl createOrder params[0].userId 12345这样过滤掉无关调用只记录目标用户的请求现场。这类操作日志和APM都很难实现同等精准度。不过要留意tt记录是有内存开销的默认上限是10000条记录。在流量很大的接口上长时间挂tt需要控制记录数量或者用-n参数限制记录条数避免内存膨胀。我的习惯是挂tt的时间不超过几分钟拿到想要的现场就立即tt --delete-all清理。5.4 跨线程调用怎么跟踪还有一个进阶话题URL请求经过线程池异步处理调用链断裂了。Arthas默认的watch/trace只跟踪当前线程内的调用链如果方法A把任务抛给线程池里的线程B执行B里的方法调用A看不到。处理这类问题有两个思路。第一种是在B的执行方法上单独挂Arthas命令看把线程池执行的方法当作新的跟踪起点。第二种是借助一些Arthas版本里提供的--thread相关能力在trace时尝试跨线程聚合。但整体上跨线程场景Arthas不是强项如果你大量使用异步编程建议上APM工具专门解决链路追踪。6. 进阶玩法Arthas的脚本化与批处理6.1 把常用诊断流程固化成脚本Arthas的命令可以批量执行这一点被很多人忽略了。你可以把一组命令写进脚本文件用-f参数批量执行java -jar arthas-boot.jar --pid 12345 -f /path/to/diagnose.as在diagnose.as文件里逐行写Arthas命令按顺序执行。这对于团队内部沉淀诊断SOP非常有帮助。比如我处理线上慢接口固定脚本大概是dashboard -i 1000 -n 5 thread -n 3 trace com.example.service.OrderServiceImpl createOrder把这三条命令串起来执行一次性拿到大盘数据、CPU热点线程和业务方法耗时分布。然后根据输出决定下一步深入方向。6.2 用async命令实现异步执行Arthas也提供了async命令可以异步执行其他命令把结果输出到指定文件。这在“现场稍纵即逝”的场景特别好用async watch com.example.service.OrderServiceImpl createOrder {params, returnObj} -x 3 -n 5 -f /tmp/watch_result.log这条命令让Arthas在后台持续监听入参和返回结果写入日志文件。你可以先让监听跑起来然后去复现问题结束后查看日志文件。等于做了一个“临时日志探针”但不需要改代码。6.3 编写自定义诊断命令的思路Arthas支持通过ognl和vmtool等命令做很灵活的定制化诊断。比如你想查看某个类当前加载的所有实例数量用vmtoolvmtool -x 2 --action getInstances --className java.lang.String虽然这个例子比较极端但思路值得借鉴把Arthas当成一个“能在JVM里执行任意代码”的调试终端而不只是那几个内置命令。遇到内置命令覆盖不了的需求用ognl和vmtool组合往往能自己造出趁手的诊断工具。7. 常见问题速查与独家排错心得7.1 命令执行报错的排查速查表现象常见原因排查路径启动时找不到目标进程jps枚举失败、用户不一致、容器隔离确认ps -ef进程存在改用--pid容器内执行attach之后提示拒绝连接目标JVM关闭了attach监听检查JVM参数是否有-XX:DisableAttachMechanismwatch/trace命令无输出类名或方法名写错或方法从未被调用用sc命令确认类是否加载用sm确认方法是否存在ognl表达式报语法错误表达式写法不对或上下文变量不存在先写一个简单的ognl表达式测试环境确认上下文变量名匹配dashboard显示乱码终端编码问题设置-Dfile.encodingUTF-8或调整终端编码trace显示的调用耗时看不出问题方法内部子调用合并显示加-n参数追踪多层或用watch方法内联参数7.2 一个完整的排查案例复盘最后用一个真实排查案例串联一下前面的知识。某个订单服务的退款接口偶发耗时超过10秒日志里没有异常只有少量的超时警告。我的排查过程是这样展开的先用thread -n 3看CPU是否异常排除了CPU飙高问题。然后在退款入口方法上挂trace发现耗时主要集中在一个查询订单明细的Mapper方法上。继续trace这个Mapper方法结果发现时间几乎全部花在等待一个数据库连接上。于是我再用thread -b查看是否有锁竞争果然发现连接池的获取锁被其他线程持有。进一步ognl查看连接池配置发现连接池最大连接数配置小于应用并发数。整个链路从现象到根因用了不到五分钟。这就是Arthas的进阶用法带来的实际价值你不光会执行命令还要形成一套“由表及里、逐层下钻”的排查思维。命令是工具思维才是核心。7.3 长期稳定使用Arthas的几条心态建议Arthas是好工具但它不是万能的。它在生产环境操作时需要谨慎尤其是reset、stop全局卸载、以及类的重新加载这类高危操作一定要判断好影响范围再执行。给生产环境用的机器上建议固定Arthas版本避免因为升级带来不必要的变化。如果你所在的团队还没有人使用Arthas建议先从低风险的线程排查、方法耗时统计这类场景入手逐步建立起团队的诊断文化。等到大家都熟悉了再推广到更复杂的字节码替换、热更新等高级场景。这样既能控制风险又能让工具价值最大化。最后再分享一个我自己的小习惯每次用Arthas定位完一个问题我都会把用到的命令和执行结果整理成一段文字贴到项目的运维文档里。长期积累下来这个文档就成了团队的“排障手册”很多重复问题都能直接按图索骥十分钟内搞定。这套组合拳打下来Arthas就不再是一个命令行工具而成了一套团队级的线上诊断资产。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门