Java诊断工具 Arthas简单方法介绍

937 阅读6分钟

前言

Arthas 是Alibaba开源的Java诊断工具,深受开发者喜爱。 官方对它的定位是帮助我们解决线上的问题。由于环境不同,经常会出现用户实际使用时出现的问题我们测试环境无法复现,我们本机调试运行正常,但是到了测试环境就出现问题了。这时候arthas就可以帮助我们快速的定位方法的调用过程,快速复现问题并及时的解决问题。同时,arthas的火焰图和trace命令还可以帮我们快速检测出方法中耗时最长的部分,利于我们优化代码效率,热编译可以帮助我们调试的时候更快的更新自己的代码,减少编译等待的时间。

文档

GitHub

官方文档

基础教程

安装

  1. 下载路径

    arthas.aliyun.com/arthas-boot…

  2. 下载

    # 推荐方法 -> 下载jar包到本地直接执行
    curl -O https://arthas.aliyun.com/arthas-boot.jar
    
    # linux/unix/mac 可以一键安装, 然后通过 ./as.sh 启动
    curl -L https://arthas.aliyun.com/install.sh | sh
    
  3. 打印帮助信息

    java -jar arthas-boot.jar -h
    
  4. 启动

    java -jar arthas-boot.jar
    # 端口被占用时可以添加参数修改 tenlet 和http的端口
    java -jar arthas-boot.jar --telnet-port 9998 --http-port -1
    

常用命令

  1. dashboard 查看系统实时数据面板

    # 每1000ms显示一次, 显示5次
    dashboard -i 1000 -n 5
    
  2. thread 查看当前线程信息, 查看线程堆栈

    # 列出5000ms内最忙的3个线程
    thread -i 5000 -n 3 
    # 查看第一页线程信息
    thread
    # 查看所有线程信息
    thread -all
    # 查看阻塞线程(目前只支持synchronized阻塞的)
    thread -b
    # 查看指定状态线程
    thread --state WAITING
    # 查看指定线程
    thread 3
    
  3. jad 反编译目标方法或类的代码

    # 反编译指定类
    jad --source-only com.example.demo.controller.TestController 
    # 反编译指定类中的方法
    jad --source-only com.example.demo.controller.TestController a
    
  4. mc

    Memory Compiler内存编译器, 编译.java生成class

    # 编译java文件 -d 指定编译文件路径
    mc E:/Downloads/Ding/demo/src/main/java/com/example/demo/controller/TestController.java -d E:/Downloads/Ding/demo/src/main/java/
    
  5. retransform

    加载外部的.class文件. 覆盖JVM已加载的类

    限制:

    • 不允许新增加field/method
    • 正在运行的函数,没有退出不能生效
    # 用编译好的class文件替换掉jvm中已加载的类
    retransform  E:/Downloads/Ding/demo/src/main/java/com/example/demo/controller/TestController.class
    # 查看retransform entry
    retransform -l
    # 删除指定retransform entry
    retransform -d 1
    # 删除所有retransform entry
    retransform --deleteAll
    # 显示触发 retransform ( entry中有最新的transform时使用最新的, 没有时还原)
    retransform --classPattern com.example.demo.controller.TestController
    

    消除retransform影响:

    • 删除这个类对应的 retransform entry ( 一般直接删除对应的entry之后类即会还原, 如果对一个类执行多次retransform, 则可能需要执行重新触发retransform命令来消除)

    • 重新触发retransform

      如果不清除掉所有的 retransform entry,并重新触发 retransform ,则arthas stop时,retransform过的类仍然生效。

  6. ognl

    文档

    # 调用静态函数
    ognl '@com.example.demo.controller.TestController@staticVoid()'
    # 调用静态类的静态字段
    ognl '@com.example.demo.controller.TestController@LOOP_COUNT'
    # 执行多行表达式,赋值给临时变量,返回一个List:
    ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #vaue2}'
    
  7. monitor

    监控项说明
    timestamp时间戳
    classJava类
    method方法(构造方法、普通方法)
    total调用次数
    success成功次数
    fail失败次数
    rt平均RT
    fail-rate失败率
    # 监视指定方法的执行记录
    monitor -c 5 com.example.demo.controller.TestController a
    # 监视的时候按照ognl表达式添加过滤 例如如下表示第一个参数为"1"时才进入 monitor
    monitor -c 5 com.example.demo.controller.TestController a 'params[0] == "1"'
    
  8. ✨watch 查看指定函数的返回值

    参数名称参数说明
    class-pattern类名表达式匹配
    method-pattern方法名表达式匹配
    express观察表达式
    condition-express条件表达式
    [b]方法调用之前观察
    [e]方法异常之后观察
    [s]方法返回之后观察
    [f]方法结束之后(正常返回和异常返回)观察
    [E]开启正则表达式匹配,默认为通配符匹配
    [x:]指定输出结果的属性遍历深度,默认为 1
    # 查看方法入参
    watch com.example.demo.controller.TestController a  '{params}' -x 3 -b
    
    # 查看{方法出参, 返回值, 抛出的异常}
    # -v 表示会打印Condition express的具体值和执行结果,方便确认
    # -n 5表示只执行5次
    # -x 3 表示参数的深度为 3, 不设置时默认深度为 1
    # params.length > 0 限定传参不为空时进入 watch
    watch com.example.demo.controller.TestController a '{params,returnObj,throwExp}' -v -n 5 -x 3 'params.length > 0'
    
    # 限定第二个参数的ID为 1 时进入 watch
    watch com.example.demo.controller.TestController a '{params,returnObj,throwExp}' -v -n 5 -x 3 'params[1].id == 1'
    
    # 查看方法调用前和调用后的参数
    # -b 在方法调用前观察
    # -s 在方法调用后观察
    watch com.example.demo.controller.TestController a '{params,returnObj,throwExp}' -v -n 5 -x 3 -b -s 'params[1].id == 1'
    
    # 按照耗时过滤, 耗时大于6000ms时进入 watch
    watch com.example.demo.controller.TestController a '{params,returnObj,throwExp}' -v -n 5 -x 3 '#cost > 6000'
    
    # 查看当前对象属性
    watch com.example.demo.controller.TestController a  'target'
    
    # 查看当前对象的常量值
    watch com.example.demo.controller.TestController a  'target.LOOP_COUNT'
    
  9. ✨profiler

    使用async-profiler生成火焰图, 找出占用时间较长的方法

    # 启动统计
    profiler start
    # 查看采样数(非必须)
    profiler getSamples
    # 结束统计
    profiler stop
    
  10. ✨trace

    显示各个方法的执行耗时

      # trace 单个方法
       trace com.example.demo.controller.TestController a -v -n 5 --skipJDKMethod false '1==1'
       
       # trace 多个方法
       trace -E com.example.demo.controller.TestController a|b|c -n 5 -v --skipJDKMethod false '1==1'
    
  11. ✨tt TimeTunnel [时间隧道]

    # 记录
    tt -t com.example.demo.controller.TestController a -n 1
    
    # 重新触发一次
    tt -p -i 1000
    
    # 获取之前执行的参数 返回值等
    tt  -w '{method.name,params,returnObj,throwExp}' -x 3 -i 1000
    
    # 周期性触发三次
    tt -p --replay-times 3 --replay-interval 2000 -i 1000
    
    # 记录springMVC 中的RequestMappingHandlerAdapter#invokeHandlerMethod的请求(因为这个对象可以通过getApplicationContext())获取到对应的值
    tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod    
    
    # 按照监视的对象获取Spring容器的ApplicationContext
    tt -i 1000 -w 'target.getApplicationContext()'
    
    # 从容器中取出任意的Bean!
    tt -i 1000 -w 'target.getApplicationContext().getBean("testController")'
    
    # 执行Bean中的方法
    tt -i 1000 -w 'target.getApplicationContext().getBean("testController").updateUserInfo(null)'
    # 执行Bean中的方法(传递参数为对象时的写法)
    tt -i 1000 -w 'target.getApplicationContext().getBean("testController").updateUserInfo((#user = new com.example.demo.pojo.User(), #user.setId(1), #user.setName("testName") , #user.setBook((#book = new com.example.demo.pojo.Book(), #book.setBookId(2), #book)), #user))'
    
    # 添加获取ApplicationContext的类后
    ognl -x 3 '#springContext=@com.example.demo.util.ApplicationContextProvider@context,#springContext.getBean("testController").printMail()'
    
    # 查看配置项信息
    ognl -x 3 '#springContext=@com.example.demo.util.ApplicationContextProvider@context,#springContext.getEnvironment().getProperty("test.value")'
    

其他

  1. IDEA插件安装

    idea插件地址

    插件使用方法

  2. 文档/社区

    用户案例

    一些特殊用法