Arthas实战:Java线上问题实时诊断与排查指南
线上服务报警CPU飙到400%日志翻了好几页也没看出来问题接口P99耗时突然从80ms涨到1秒灰度回滚还是偶发一个OOM在测试环境始终无法复现重启就丢现场……这些场景只要你是搞Java的大概率都经历过。我之前遇到这类问题第一反应是jstack、jmap、加日志、重启验证至少折腾半小时起步。后来用上Arthas整个排查效率直接上了一个台阶——不需要重启应用不需要改代码attach到正在运行的JVM上就能实时诊断线程栈、方法调用链、入参返回值、甚至热更新代码一个交互式终端里全搞定。Arthas是阿里开源的Java诊断工具核心价值就是那四个字实时诊断。这篇文章我就结合自己用Arthas排过的真实故障从安装启动、常用命令、实战案例到常见坑位一次性写清楚。不管你是开发、运维还是QA只要干的是Java生态的活这工具都应该进你的工具箱。1. 为什么需要Arthas传统排查手段的痛点1.1 线上问题排查的常见老大难我平时收到最多的求助就是这个接口特别慢但日志正常、 内存一直涨但不报错 、这个方法偶尔返回空结果但不知道什么入参触发的。这种问题有几个共同特点第一日志根本没有打印关键上下文比如某个方法的具体入参、内部子调用的耗时分布第二问题在测试环境难以复现只有在生产环境的高并发、特定数据组合下才会触发第三线上服务不能随便重启一旦重启线程状态、堆内存、锁竞争这些现场全部丢失。传统思路无非是加日志再发版或者登录服务器敲jstack、jmap抓快照。但加日志需要重新部署不仅慢而且你未必能猜到该在哪个方法加jstack和jmap虽然能拿到线程栈和堆转储但它们是一次性快照看不到方法内部的调用次数、耗时占比也不知道某个线程在一个时间窗口内连续执行了什么。有一次我抓了三份jstack对比半天才勉强猜出是哪个线程在空转。1.2 Arthas能做什么不能做什么Arthas解决的正是现场不可复现、重启代价大、静态快照不够用这几个问题。它可以attach到一个正在运行的Java进程上相当于打开了一个交互式诊断窗口。在这个窗口里你能看到所有线程的实时状态能观察到某个方法每次调用的入参和返回值能追踪一次方法调用内部每个子环节的耗时甚至能在JVM不重启的前提下直接替换正在运行的方法体。它的“实时诊断”能力让你可以持续观察而不是只拿一个时间切片。但Arthas不是万能的。它不会自动告诉你哪里有问题需要你自己判断该看什么它也不适合做7x24小时的持续监控那是APM和监控系统的事。我更愿意把它类比成显微镜——监控系统是雷达告诉你什么区域可能有敌情而Arthas是显微镜让你把可疑的那一个细胞放大看清楚。所以日常监控照常做出了问题用Arthas切入定位根因。这个定位过程是交互式、可反复验证的这是它和传统工具最大的区别。1.3 和传统排查工具的核心对比拿几个常用工具和Arthas做个对比能更直观看出差异。工具是否需要重启能否观察方法级调用能否修改运行时代码主要用途jstack否否否打印线程栈快照jmap否否否导出堆转储查看堆内存日志通常需要需要预埋点否业务运行记录Arthas否是是动态诊断和修复从表格里能看到传统工具要么是静态快照要么需要提前埋点。Arthas把看现场和动刀子合到了一起而且整个过程不需要重启JVM。这一点在生产环境尤为重要毕竟现在的微服务架构里重启一个实例可能涉及注册中心、配置中心、分布式缓存一系列联动代价不小。2. 安装与启动从下载到成功attach2.1 下载与启动方式Arthas的安装非常轻量没有服务端不需要改项目代码就是一个jar包加几个辅助脚本。官方推荐的方式是直接下载arthas-boot.jar然后用java -jar启动。下载的时候建议去GitHub Releases或者官方文档里找最新的stable版本不要用博客里分享的旧链接版本太老会遇到JDK版本兼容问题。启动命令很简单java -jar arthas-boot.jar执行后Arthas会列出当前机器上所有它能识别到的Java进程你输入序号就能attach到目标进程。整个过程是交互式的默认在本机开启一个telnet端口用于通信。如果机器上只有一个Java进程也是一样输入1回车就会进入Arthas控制台。除此之外还有一种方式是使用as.sh脚本启动脚本内部还是调用arthas-boot.jar只是封装了一层适合在Linux服务器上使用。但我实际用下来直接执行arthas-boot.jar更稳定特别是多用户环境下脚本偶尔会碰到路径解析和权限问题。Arthas还支持以非交互模式运行比如在脚本里通过-c参数指定要执行的命令但日常排障还是交互模式用得最多。2.2 启动时进程列表为空的排查方法经常有人在群里问执行arthas-boot.jar后列出来的进程列表是空的或者看不到自己的目标应用这怎么解决这个问题我遇到过好几次。Arthas能够发现进程依赖的是JDK的attach机制而这个机制会读取目标JVM在临时目录下生成的socket文件通常在/tmp/hsperfdata_xxx目录下。如果这个目录的权限不对或者被系统定时清理掉就会导致进程列表为空或者attach失败。排查步骤我一般这样处理先确认当前执行Arthas的用户和目标Java进程的启动用户是否一致不一致就切换用户再试。执行ls -la /tmp/hsperfdata_*查看目录是否存在且有权限。如果是在容器环境确认是否在同一个容器里执行。还有一个容易被忽视的情况Linux系统的/tmp目录使用tmpfs时重启机器后临时文件会清空。这种情况建议在启动Java应用时显式指定临时目录-Djava.io.tmpdir/app/tmp并且保证目录存在、可写。Arthas的attach机制依赖的就是这个目录路径对了才能顺利列出进程。2.3 权限与attach失败的注意事项attach过程中最常见的报错是“Unable to open socket file”或者“connection refused”。原因基本跑不出两个一是权限不足当前用户对目标进程或/tmp目录下的文件没有读写权限二是容器隔离问题进程在容器里但你从宿主机上执行ArthasPID命名空间和文件系统都隔离了自然连不上。对于权限问题切到应用启动用户再执行Arthas基本能解决。对于容器环境正确做法是进入容器内部再操作。Docker就docker exec -it xxx bashKubernetes就kubectl exec -it pod-name -- /bin/bash。现在很多Java服务都是容器化部署直接在宿主机上用Arthas往往连不上这个坑我反复提醒团队不要在宿主机上干着急。多用户环境里还有个细节如果多个用户都在服务器上部署了Java应用你用当前账号会看到一堆别人的进程有些你能attach有些不能。为了减少干扰我习惯先在应用部署目录下用ps -ef | grep java确认PID再在Arthas的进程列表里直接找对应PID而不去看应用名避免认错进程。2.4 Arthas的交互式会话管理进入Arthas控制台后整个会话是附着在目标JVM上的。我用完一般会执行stop命令来关闭Arthas服务端或者直接quit退出客户端。需要注意quit只是退出当前交互客户端Arthas的服务端可能还在后台运行占用telnet端口stop才会彻底结束。如果不小心关掉了终端Arthas服务端可能还挂着下次再连会发现端口被占用。Arthas还支持在启动时指定telnet端口和http端口默认是3658和8563。如果服务器上同时有多个Arthas实例端口可能冲突最好显式指定。在生产环境建议配置认证信息。官方支持通过telnet端口密码和http端口鉴权虽然增加了一点使用成本但能防止有人随意attach到生产JVM上执行高危操作。Arthas本身是一个非常强大的工具没有权限管控等于把手术刀随便放桌上这个风险不值得冒。3. 这些命令和场景是Arthas的看家本领3.1 dashboard与thread先看全局状态进入控制台后第一步我一般执行dashboard命令。它会展示JVM的实时数据面板包括CPU使用率、内存使用情况、GC次数和耗时、线程数量等信息。这个命令相当于一个快速体检能帮你判断当前问题的方向是CPU、内存还是线程。看完全局再看线程详情。thread命令是诊断线程问题的核心它的灵活用法非常多。比如查看CPU占用最高的前3个线程thread -n 3这个输出会直接显示线程ID、CPU占用率、线程状态和当前堆栈。CPU飙高时这一步往往就能定位到问题线程。另外我经常用thread --state TIMED_WAITING来查看有多少线程处于等待状态如果这类线程特别多再结合线程池参数就能判断是不是线程池被耗尽了。还有一个关键命令是thread -b它专门用来查找阻塞其他线程的线程也就是死锁场景下的元凶。高并发环境里多个线程互相等待锁用thread -b可以直接看到当前JVM中谁持有了锁、谁在等待省去了手工分析线程间依赖关系的过程。3.2 watch与trace定位方法级性能瓶颈如果你已经定位到某个接口慢但不知道慢在哪一步trace命令是首选。它可以输出指定方法内部每一个子方法的调用耗时和调用次数。举个例子trace com.example.OrderService createOrder命令执行后每次createOrder方法被调用控制台就会打印一次内部调用链包括每个子方法的耗时、总耗时和异常信息。我在排查一次接口超时问题时就是用trace发现耗时主要集中在一个Redis批量查询方法上而这个方法内部还循环发了多次MGET请求网络往返成了瓶颈。watch命令主要负责观察方法参数、返回值和异常。常见的用法watch com.example.OrderService createOrder {params, returnObj, throwExp} -x 3这里的-x参数表示展开结果的深度避免对象嵌套过深导致输出爆炸。这个方法在定位偶发NPE时特别有用因为很多NPE在日志里只打了一行异常堆栈没有打出入参你根本不知道是哪个参数为空。用watch挂上目标方法后参数值一目了然。我经常把watch理解为给方法装了一个临时日志点而且不用改代码、不用发版。需要强调一下watch和trace是通过字节码增强技术临时织入逻辑的长时间挂载会带来性能开销。我自己使用时的习惯是确认问题窗口期后立刻使用拿到线索马上stop尽量减少对线上服务的影响。3.3 jadmcretransform不重启热更新线上代码这一套组合拳是Arthas里让我觉得最惊艳的部分。它的应用场景是线上有个小bug改一行代码的事但重新发版要排队、要审核等不起这时候可以先用jad命令反编译目标类jad --source-only com.example.OrderService /tmp/OrderService.java然后编辑这个Java文件把逻辑修好。接着使用sc命令查看类加载器信息再用mc命令把修改后的文件编译成classmc /tmp/OrderService.java -d /tmp最后用retransform命令让JVM加载新的classretransform /tmp/com/example/OrderService.class这样目标方法就完成了热替换整个过程JVM不会重启连接也不会断。但这里有一个重要限制热更只允许修改方法体内的逻辑不允许增加方法、删除方法、修改方法签名或修改字段结构。因为JVM在运行时不允许改变类的结构只能替换实现逻辑。所以这种方案适合修逻辑缺陷不适合做结构性调整。我在生产环境用热更时会提前把原来编译好的class文件备份到一个目录。万一新逻辑有问题可以通过retransform原class一键回滚。另外还要注意如果字段是static final修饰的常量并且已经在编译期内联到其他类里就算热更改了字段值引用的地方也不会生效这种坑属于改了等于没改。3.4 sc、getstatic与ognl探索运行期对象状态诊断过程中经常要确认当前这个类的状态到底是什么比如某个配置类加载了哪些变量、某个单例是否已经初始化。这时候用到的是sc命令。sc可以查看类的详细信息包括类加载器、注解、修饰符等sc -d com.example.OrderService输出里会显示ClassLoader的hashcode这对确认类由哪个类加载器加载非常重要尤其在Spring Boot或OSGi环境里同名类可能有多个版本。Arthas执行命令时我们还可以通过-c参数指定类加载器的hashcode避免命令作用到错误的类上。如果想查看某个类的静态字段当前值可以用getstatic命令。比如getstatic com.example.OrderService config这会返回OrderService类中config静态字段的内容。如果你还想更灵活地执行一段表达式那就要用到ognl命令。ognl是Arthas里一个非常强大的表达式工具可以调用对象方法、访问字段、甚至调用一个静态方法。比如ognl com.example.OrderServicegetConfig()我通常在排查配置项为什么没生效时用它直接看运行期的实际值跟配置文件比对马上就能知道是配置中心没推送还是本地缓存覆盖了。ognl的写法有点学习成本但掌握了之后会发现它几乎是在JVM里执行任意代码的入口前提是你对自己的目标对象结构足够熟悉。4. 实战案例拆解CPU飙高、接口超时和内存异常4.1 案例一CPU飙高定位问题线程有一回线上告警某个Java服务CPU使用率升到400%但流量没什么波动。我在服务器上先用top看到Java进程PID再用top -Hp PID查线程级CPU发现有一个线程占用持续偏高。这种场景下如果拿jstack去看只能看到当前瞬间的线程栈不一定能抓到正在空转的那个线程。Arthas上场后执行thread -n 1它会按CPU占用倒序显示最忙的线程并直接打印堆栈。结果一下就看到了代码栈某个函数正在执行正则表达式匹配输入是一段很长的字符串而正则表达式写法存在灾难性回溯问题。这种问题靠日志完全看不出来因为每个请求都正常返回了只是耗时特别长、CPU空转。修复方式就是换用更安全的正则写法或者加长度校验。这次排障给我印象很深从登录服务器到定位到具体代码行总共就用了几分钟。之前用jstack的做法是隔几秒打一次线程栈打三四份再慢慢对比不仅慢还容易漏掉正在执行的线程。4.2 案例二接口超时与watch排查另一个案例是某个接口偶发超时监控显示P99耗时从80ms涨到1s但日志里唯一的异常信息是下游超时。刚开始大家都怀疑是不是下游系统不稳定但下游系统反馈耗时正常。为了找到真实原因我用trace命令跟踪了接口入口方法看到耗时主要集中在一次RPC调用上这是预期内的。然后用watch命令去观察这次RPC调用的入参。不看不知道一看发现其中一个参数是数组里面塞了大量重复元素下游系统要对这个数组做全量循环处理性能自然扛不住。顺着数组内容往上追定位到是上游一个循环逻辑在最近一次发版时误把全量数据传了进去。这种问题如果用传统方式需要在下游加日志再发版等下一次偶发复现至少要一两天而Arthas在问题发生时就能直接抓到异常入参排查效率完全不在一个量级。4.3 案例三内存不足OutOfMemoryError的初步诊断OOM问题算是Java排查里比较麻烦的一类。有一次测试环境报Java heap space不足但测试环境不允许重启因为一重启问题复现的条件可能就变了。我先用dashboard命令看堆内存和GC频率发现老年代在持续增长Minor GC和Full GC频率都不正常这说明大概率是某个长期存活对象无法被回收。为了拿到更详细的内存现场我用了memory命令查看堆内各个内存区域的使用情况紧接着用heapdump导出堆转储文件heapdump /tmp/heap.hprof导出的文件用MAT分析后发现是一个静态Map里缓存了大量业务对象而缓存没有设置过期时间导致内存无限增长。Arthas在这个案例里主要负责快速获取现场——先用dashboard判断是否需要dump再用heapdump在同一个会话里完成导出全程没有重启服务。相比直接用jmap操作路径更短也更容易在紧急时刻快速响应。4.4 案例四热更新修复线上NPE的一次实践有一次线上接口偶发NPE日志里堆栈指向一个工具类的某个方法但看不到入参。由于是周五傍晚发版流程已经冻结不能用常规手段修复。我用了watch命令挂上这个方法确认是有一个参数为空导致NPE而这行代码其实就是少了一个判空逻辑。修复方案很直接用jad反编译出这个类的源码加上判空后重新回到默认值再用mc编译、retransform热更。整个操作大概十五分钟没有重启应用接口恢复正常。这应该是我在生产环境做得最大胆又最小心的一次操作。说大胆是因为直接改了线上运行的代码说小心是因为我提前备份了原class并且热更后一直盯着监控指标。热更虽然方便但不是常规手段它能帮你在紧急时刻应急不能取代正常的代码评审和发版流程。5. 常见问题速查与避坑心得5.1 高频问题排查对照表Arthas用久了总有一些反复出现的问题。我整理了一份自查表基本覆盖了大家常遇到的场景。现象可能原因处理建议进程列表为空当前用户与目标进程用户不一致或/tmp目录被清理切换用户或使用sudo检查/tmp/hsperfdata_*attach失败socket报错权限不足或容器PID命名空间隔离进入容器内部执行确保目录可写命令执行后无输出方法签名不匹配或类被多个ClassLoader加载用sc -d查看类加载器需要时用-c指定trace不显示子调用目标方法没有实际执行或trace层级不够确认调用是否发生或用-n参数限制观察次数热更新不生效修改了字段结构或static final常量内联只改方法体避免修改字段和签名这张表里的每一条我基本都在项目里帮人排查过。其中类被多个ClassLoader加载是最容易踩的坑尤其Spring Boot应用会有LaunchedURLClassLoader再到Tomcat容器又有一层WebAppClassLoader同一个类名可能对应多个版本。如果你trace的方法不是目标ClassLoader加载的命令看起来执行了实际上什么都没观察到。处理方式就是先用sc -d看ClassLoader hashcode再在watch、trace命令里用-c参数指定正确的ClassLoader。5.2 我在生产环境使用Arthas的一些习惯用Arthas这些年我逐渐形成了一套自己的使用习惯这里分享给同行。第一尽量不在高峰期长时间挂载watch和trace虽然Arthas本身开销很小但字节码增强和输出打印还是会占CPU和IO建议先确认问题窗口再开启定位完立刻stop。第二热更前必须备份原class文件并且先在预发环境验证一遍没有十足的把握不要在生产环境动刀。第三生产环境务必开启Arthas的认证配置否则任何能访问内网端口的人都可以操作你的JVM风险太大。另外在团队协作方面也值得注意。多个人共用一台服务器时如果你attach到某个进程别人也attach进来互相执行的命令会交叉干扰输出的结果就不好判断是谁造成的。我后来在团队里立了个规矩Arthas操作尽量通过统一的运维平台或管理账号执行操作前在群里说一声避免多人同时操作同一个进程。5.3 在面试中提起Arthas时的加分点Arthas也是Java面试里的高频话题尤其是和线上问题排查相关的八股文面试官动不动就会问线上CPU飙高你怎么办、如果接口变慢你怎么定位。很多人会背答案说top、jstack但如果你能结合Arthas来讲会显得你确实有实战经验。比如你可以这样说先用top和top -Hp找到最忙线程再通过Arthas的thread -n命令直接查看线程栈或者用trace命令在方法级别定位耗时瓶颈。这样既展示了工具掌握度也体现了排查思路。面试里还有一个常问的点是有没有不用重启就能修复线上问题的手段这就是Arthas热更的应用场景。不过提热更的时候一定要强调你清楚它的局限性比如只能改方法体、不能改结构以及生产环境使用的风险。面试官其实不指望你真的在生产环境经常热更但如果你能讲清楚原理、限制和R回滚方案那印象分一定不会低。这比单纯背一堆命令行要真实得多。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →