• 学会 arthas,让你 3 年经验掌握 5 年功力!


    简介

    Arthas 是Alibaba开源的Java诊断工具,动态跟踪Java代码;实时监控JVM状态,可以在不中断程序执行的情况下轻松完成JVM相关问题排查工作 。支持JDK 6+,支持Linux/Mac/Windows。这个工具真的很好用,而且入门超简单,十分推荐。

    使用场景

    1. 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?

    2. 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?

    3. 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?

    4. 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!

    5. 是否有一个全局视角来查看系统的运行状况?

    6. 有什么办法可以监控到JVM的实时运行状态?

    接下来,围绕这6个问题,学习下Arthas的基本用法。

    安装

    执行下面命令下载

    wget https://alibaba.github.io/arthas/arthas-boot.jar
    

    用java -jar的方式启动

    1. java -jar arthas-boot.jar
    2. [INFO] Found existing java process, please choose one and hit RETURN.
    3. * [1]: 79952 cn.test.MobileApplication
    4.   [2]: 93872 org.jetbrains.jps.cmdline.Launcher

    然后输入数字,选择你想要监听的应用,回车即可

    也可点此下载arthas相关jar:http://repository.sonatype.org/service/local/artifact/maven/redirect?r=central-proxy&g=com.taobao.arthas&a=arthas-packaging&e=zip&c=bin&v=LATEST

    常用命令

    当前版本v3.1.4

    1. [arthas@79952]version
    2. 3.1.4

    1、stack

    输出当前方法被调用的调用路径

    很多时候我们都知道一个方法被执行,但是有很多地方调用了它,你并不知道是谁调用了它,此时你需要的是 stack 命令。

    参数名称参数说明
    class-pattern类名表达式匹配
    method-pattern方法名表达式匹配
    1. [arthas@79952]$ stack com.baomidou.mybatisplus.extension.service.IService getOne
    2. Press Q or Ctrl+C to abort.
    3. Affect(class-cnt:202 method-cnt:209) cost in 10761 ms.
    4. ts=2019-11-13 11:49:13;thread_name=http-nio-8801-exec-6;id=2d;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@a6c54c3
    5.     @com.baomidou.mybatisplus.extension.service.impl.ServiceImpl.getOne()
    6.         at com.baomidou.mybatisplus.extension.service.IService.getOne(IService.java:230)
    7.         ...... ......
    8.         at cn.test.mobile.controller.order.OrderController.getOrderInfo(OrderController.java:500)

    可以看到OrderController.java的第500行调用了这个getOne接口。

    注意这个命令需要调用后才会触发日志,相似的还有watch、trace等

    2、jad

    反编译指定已加载类的源码

    有时候,版本发布后,代码竟然没有执行,代码是最新的吗,这时可以使用jad反编译相应的class。

    jad cn.test.mobile.controller.order.OrderController
    

    仅编译指定的方法

    1. jad cn.test.mobile.controller.order.OrderController getOrderInfo
    2. ClassLoader:
    3. @RequestMapping(value={"getOrderInfo"}, method={RequestMethod.POST})
    4. public Object getOrderInfo(HttpServletRequest request, @RequestBody Map map) {
    5.     ResponseVo responseVo = new ResponseVo();
    6.     ... ... ...  ...

    3、sc

    “Search-Class” 的简写 ,查看JVM已加载的类信息 有的时候,你只记得类的部分关键词,你可以用sc获取完整名称 当你碰到这个错的时候“ClassNotFoundException”或者“ClassDefNotFoundException”,你可以用这个命令验证下

    参数名称参数说明
    class-pattern类名表达式匹配
    method-pattern方法名表达式匹配
    [d]输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的ClassLoader等详细信息。如果一个类被多个ClassLoader所加载,则会出现多次

    模糊搜索

    1. sc *OrderController*
    2. cn.test.mobile.controller.order.OrderController

    打印类的详细信息 sc -d

    1. sc -d cn.test.mobile.controller.order.OrderController
    2.  class-info        cn.test.mobile.controller.order.OrderController
    3.  code-source       /F:/IDEA-WORKSPACE-TEST-qyb/trunk/BE/mobile/target/classes/
    4.  name              cn.test.mobile.controller.order.OrderController
    5.  isInterface       false
    6.  isAnnotation      false
    7.  isEnum            false
    8.  isAnonymousClass  false
    9.  isArray           false
    10.  isLocalClass      false
    11.  isMemberClass     false
    12.  isPrimitive       false
    13.  isSynthetic       false
    14.  simple-name       OrderController
    15.  modifier          public
    16.  annotation        org.springframework.web.bind.annotation.RestController,org.springframework.web.bind.annotation.Requ
    17.                    estMapping
    18.  interfaces
    19.  super-class       +-cn.test.mobile.controller.BaseController
    20.                      +-java.lang.Object
    21.  class-loader      +-sun.misc.Launcher$AppClassLoader@18b4aac2
    22.                      +-sun.misc.Launcher$ExtClassLoader@480bdb19
    23.  classLoaderHash   18b4aac2

    与之相应的还有sm( “Search-Method” ),查看已加载类的方法信息

    查看String里的方法

    1. sm java.lang.String
    2. java.lang.String ([BII)V
    3. java.lang.String ([BLjava/nio/charset/Charset;)V
    4. java.lang.String ([BLjava/lang/String;)V
    5. java.lang.String ([BIILjava/nio/charset/Charset;)V
    6. java.lang.String ([BIILjava/lang/String;)V
    7. ... ... ... ...

    查看String中toString的详细信息

    1. sm -d java.lang.String toString
    2. declaring-class  java.lang.String
    3.  method-name      toString
    4.  modifier         public
    5.  annotation
    6.  parameters
    7.  return           java.lang.String
    8.  exceptions
    9.  classLoaderHash  null

    4、watch

    可以监测一个方法的入参和返回值

    有些问题线上会出现,本地重现不了,这时这个命令就有用了

    参数名称参数说明
    class-pattern类名表达式匹配
    method-pattern方法名表达式匹配
    express观察表达式
    condition-express条件表达式
    [b]「方法调用之前」观察
    [e]「方法异常之后」观察
    [s]「方法返回之后」观察
    [f]「方法结束之后」(正常返回和异常返回)观察,「默认选项」
    [E]开启正则表达式匹配,默认为通配符匹配
    [x:]指定输出结果的属性遍历深度,默认为 1

    观察getOrderInfo的出参和返回值,出参就是方法结束后的入参

    1. watch cn.test.mobile.controller.order.OrderController getOrderInfo "{params,returnObj}" -x 2
    2. Press Q or Ctrl+C to abort.
    3. Affect(class-cnt:1 , method-cnt:1) cost in 456 ms.
    4. ts=2019-11-13 15:30:18; [cost=18.48307ms] result=@ArrayList[
    5.     @Object[][  # 这个就是出参,params
    6.         @RequestFacade[org.apache.catalina.connector.RequestFacade@1d81dbd7],
    7.         @LinkedHashMap[isEmpty=false;size=2], # 把遍历深度x改为3就可以查看map里的值了
    8.     ],
    9.     @ResponseVo# 这个就是返回值 returnObj
    10.         log=@Logger[Logger[cn.test.db.common.vo.ResponseVo]],
    11.         success=@Boolean[true],
    12.         message=@String[Ok],
    13.         count=@Integer[0],
    14.         code=@Integer[1000],
    15.         data=@HashMap[isEmpty=false;size=1],
    16.     ],
    17. ]

    观察getOrderInfo的入参和返回值

    1. watch cn.test.mobile.controller.order.OrderController getOrderInfo "{params,returnObj}" -x 3 -b
    2. Press Q or Ctrl+C to abort.
    3. Affect(class-cnt:method-cnt:1) cost in 93 ms.
    4. ts=2019-11-13 15:37:38; [cost=0.012479ms] result=@ArrayList[
    5.     @Object[][
    6.         @RequestFacade[
    7.             request=@Request[org.apache.catalina.connector.Request@d04e652],
    8.             sm=@StringManager[org.apache.tomcat.util.res.StringManager@7ae7a97b],
    9.         ],
    10.         @LinkedHashMap[
    11.             @String[payNo]:@String[190911173713755288],
    12.             @String[catalogId]:@String[6],
    13.         ],
    14.     ],
    15.     null,# -b是方法调用之前观察,所以还没有返回值
    16. ]

    如果需要捕捉异常的话,使用throwExp,如{params,returnObj,throwExp}

    5、trace

    输出方法内部调用路径,和路径上每个节点的耗时

    可以通过这个命令,查看哪些方法耗性能,从而找出导致性能缺陷的代码,这个耗时还包含了arthas执行的时间哦。

    参数名称参数说明
    class-pattern类名表达式匹配
    method-pattern方法名表达式匹配
    condition-express条件表达式
    [E]开启正则表达式匹配,默认为通配符匹配
    [n:]命令执行次数
    #cost方法执行耗时

    输出getOrderInfo的调用路径

    1. trace -j cn.test.mobile.controller.order.OrderController getOrderInfo
    2. Press Q or Ctrl+C to abort.
    3. Affect(class-cnt:1 , method-cnt:1) cost in 92 ms.
    4. ---ts=2019-11-13 15:46:59;thread_name=http-nio-8801-exec-4;id=2b;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@a6c54c3
    5.     ---[15.509011ms] cn.test.mobile.controller.order.OrderController:getOrderInfo()
    6.         +---[0.03584ms] cn.test.db.common.vo.ResponseVo:() #472
    7.         +---[0.00992ms] java.util.HashMap:() #473
    8.         +---[0.02176ms] cn.test.mobile.controller.order.OrderController:getUserInfo() #478
    9.         +---[0.024ms] java.util.Map:get() #483
    10.         +---[0.00896ms] java.lang.Object:toString() #483
    11.         +---[0.00864ms] java.lang.Integer:parseInt() #483
    12.         +---[0.019199ms] com.baomidou.mybatisplus.core.conditions.query.QueryWrapper:() #500
    13.         +---[0.135679ms] com.baomidou.mybatisplus.core.conditions.query.QueryWrapper:allEq() #500
    14.         +---[12.476072ms] cn.test.db.service.IOrderMediaService:getOne() #500
    15.         +---[0.0128ms] java.util.HashMap:put() #501
    16.         +---[0.443517ms] cn.test.db.common.vo.ResponseVo:setSuccess() #503
    17.         `---[0.03488ms] java.util.Map:put() #504

    输出getOrderInfo的调用路径,且cost大于10ms,-j是指过滤掉jdk中的方法,可以看到输出少了很多

    1. trace -j cn.test.mobile.controller.order.OrderController getOrderInfo '#cost > 10'
    2. Press Q or Ctrl+C to abort.
    3. Affect(class-cnt:1 , method-cnt:1) cost in 96 ms.
    4. ---ts=2019-11-13 15:53:42;thread_name=http-nio-8801-exec-2;id=29;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@a6c54c3
    5.     ---[13.803743ms] cn.test.mobile.controller.order.OrderController:getOrderInfo()
    6.         +---[0.01312ms] cn.test.db.common.vo.ResponseVo:() #472
    7.         +---[0.01408ms] cn.test.mobile.controller.order.OrderController:getUserInfo() #478
    8.         +---[0.0128ms] com.baomidou.mybatisplus.core.conditions.query.QueryWrapper:() #500
    9.         +---[0.303998ms] com.baomidou.mybatisplus.core.conditions.query.QueryWrapper:allEq() #500
    10.         +---[12.675431ms] cn.test.db.service.IOrderMediaService:getOne() #500
    11.         `---[0.409917ms] cn.test.db.common.vo.ResponseVo:setSuccess() #503

    6、jobs

    执行后台异步任务

    线上有些问题是偶然发生的,这时就需要使用异步任务,把信息写入文件。

    使用 & 指定命令去后台运行,使用 > 将结果重写到日志文件,以trace为例

    trace -j cn.test.mobile.controller.order.OrderController getOrderInfo > test.out &
    

    jobs——列出所有job

    1.  jobs
    2. [76]*  
    3.        Running           trace -j cn.test.mobile.controller.order.OrderController getOrderInfo >> test.out &
    4.        execution count : 0
    5.        start time      : Wed Nov 13 16:13:23 CST 2019
    6.        timeout date    : Thu Nov 14 16:13:23 CST 2019
    7.        session         : f4fba846-e90b-4234-959e-e78ad0a5db8c (current)

    job id是76, * 表示此job是当前session创建,状态是Running,execution count是执行次数,timeout date是超时时间

    异步执行时间,默认为1天,如果要修改,使用options命令,

    options job-timeout 2d
    

    options可选参数 1d, 2h, 3m, 25s,分别代表天、小时、分、秒

    kill——强制终止任务

    1. kill 76
    2. kill job 76 success

    最多同时支持8个命令使用重定向将结果写日志

    请勿同时开启过多的后台异步命令,以免对目标JVM性能造成影响

    7、logger

    查看logger信息,更新logger level

    查看

    1. logger
    2.  name                ROOT
    3.  class               ch.qos.logback.classic.Logger
    4.  classLoader         sun.misc.Launcher$AppClassLoader@18b4aac2
    5.  classLoaderHash     18b4aac2 #改日志级别时要用到它
    6.  level               INFO
    7.  effectiveLevel      INFO
    8.  ... ... ... ...

    更新日志级别

    1. logger --name ROOT --level debug
    2. update logger level success.

    如果执行这个命令时出错:update logger level fail.

    指定classLoaderHash重试一下试试

    1. logger -c 18b4aac2 --name ROOT --level debug
    2. update logger level success.

    8、dashboard

    查看当前系统的实时数据面板 这个命令可以全局的查看jvm运行状态,比如内存和cpu占用情况

    1. dashboard
    2. ID        NAME                          GROUP               PRIORITY STATE     %CPU      TIME      INTERRUPT DAEMON
    3. 17        Abandoned connection cleanup  main                5        TIMED_WAI 0         0:0       false     true
    4. 1009      AsyncAppender-Worker-arthas-system              5        WAITING   0         0:0       false     true
    5. 5         Attach Listener               system              5        RUNNABLE  0         0:0       false     true
    6. 23        ContainerBackgroundProcessor[ main                5        TIMED_WAI 0         0:0       false     true
    7. 55        DestroyJavaVM                 main                5        RUNNABLE  0         0:11      false     false
    8. 3         Finalizer                     system              8        WAITING   0         0:0       false     true
    9. 18        HikariPool-1 housekeeper      main                5        TIMED_WAI 0         0:0       false     true
    10. 39        NioBlockingSelector.BlockPoll main                5        RUNNABLE  0         0:0       false     true
    11. 2         Reference Handler             system              10       WAITING   0         0:0       false     true
    12. 4         Signal Dispatcher             system              9        RUNNABLE  0         0:0       false     true
    13. 69        System Clock                  main                5        TIMED_WAI 0         0:34      false     true
    14. 25        Thread-2                      main                5        TIMED_WAI 0         0:0       false     false
    15. 37        Timer-0                       main                5        TIMED_WAI 0         0:0       false     true
    16. Memory                    used    total    max     usage    GC
    17. heap                      216M    415M     3614M   5.99%    gc.ps_scavenge.count          96
    18. ps_eden_space             36M     78M      1276M   2.90%    gc.ps_scavenge.time(ms)       3054
    19. ps_survivor_space         17M     38M      38M     46.53%   gc.ps_marksweep.count         4
    20. ps_old_gen                161M    298M     2711M   5.97%    gc.ps_marksweep.time(ms)      804
    21. nonheap                   175M    180M     -1      97.09%
    22. code_cache                35M     35M      240M    14.85%

    ID: Java级别的线程ID,注意这个ID不能跟jstack中的nativeID一一对应 我们可以通过 thread id 查看线程的堆栈 信息

    1. thread 2
    2. "Reference Handler" Id=2 WAITING on java.lang.ref.Reference$Lock@66ad4272
    3.     at java.lang.Object.wait(Native Method)
    4.     -  waiting on java.lang.ref.Reference$Lock@66ad4272
    5.     at java.lang.Object.wait(Object.java:502)
    6.     at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    7.     at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

    NAME: 线程名

    GROUP: 线程组名

    PRIORITY: 线程优先级, 1~10之间的数字,越大表示优先级越高

    STATE: 线程的状态

    CPU%: 线程消耗的cpu占比,采样100ms,将所有线程在这100ms内的cpu使用量求和,再算出每个线程的cpu使用占比。

    TIME: 线程运行总时间,数据格式为分:秒

    INTERRUPTED: 线程当前的中断位状态

    DAEMON: 是否是daemon线程

    9、redefine

    redefine jvm已加载的类 ,可以在不重启项目的情况下,热更新类。

    这个功能真的很强大,但是命令不一定会成功

    下面我们来模拟:假设我想修改OrderController里的某几行代码,然后热更新至jvm:

    a. 反编译OrderController,默认情况下,反编译结果里会带有ClassLoader信息,通过--source-only选项,可以只打印源代码。方便和mc/redefine命令结合使用

    jad --source-only cn.test.mobile.controller.order.OrderController > OrderController.java
    

    生成的OrderController.java在哪呢,执行pwd就知道在哪个目录了

    b. 查找加载OrderController的ClassLoader

    1. sc -d cn.test.mobile.controller.order.OrderController | grep classLoaderHash
    2. classLoaderHash   18b4aac2

    c. 修改保存好OrderController.java之后,使用mc(Memory Compiler)命令来编译成字节码,并且通过-c参数指定ClassLoader

    mc -c 18b4aac2 OrderController.java -d ./
    

    d. 热更新刚才修改后的代码

    1. redefine -c 18b4aac2 OrderController.class
    2. redefine success, size: 1

    然后代码就更新成功了。

    redefine 我参考了这篇文档https://github.com/alibaba/arthas/issues/537

    更多高级的功能请移步Arthas官网文档https://alibaba.github.io/arthas/quick-start.html

    其他

    如果java -jar选择启动某个应用的时候,报下面的错

    1. java -jar arthas-boot.jar
    2. [INFO] arthas-boot version: 3.1.4
    3. [INFO] Process 11544 already using port 3658
    4. [INFO] Process 11544 already using port 8563
    5. [INFO] Found existing java process, please choose one and hit RETURN.
    6. * [1]: 11544
    7.   [2]: 119504 cn.test.MobileApplication
    8.   [3]: 136340 org.jetbrains.jps.cmdline.Launcher
    9.   [4]: 3068
    10. 2 #选择第2个启动
    11. [ERROR] Target process 119504 is not the process using port 3658, you will connect to an unexpected process.
    12. [ERROR1. Try to restart arthas-boot, select process 11544, shutdown it first with running the 'shutdown' command.
    13. [ERROR2. Or try to use different telnet port, for example: java -jar arthas-boot.jar --telnet-port 9998 --http-port -1

    注意提示[ERROR] 1,只需要进入11544这个应用,然后执行shutdown关闭这个应用就可以启动了。

    学会 arthas,让你 3 年经验掌握 5 年功力!

     

     

  • 相关阅读:
    篇1:Mapbox Style Specification
    Java版分布式微服务云开发架构 Spring Cloud+Spring Boot+Mybatis 电子招标采购系统功能清单
    【Android学习】Android studio环境搭建-解决下载gradle慢&加载mainfest.xml慢的问题
    office的excel中使用,告诉我详细的解决方案,如何变成转化为金额格式
    搜狗微信APP逆向(二)so层
    【XTDrone Ubuntu20.04】XTDrone+ Ubuntu20.04 + PX4安装
    SortTable.js + vxe-table 实现多条批量排序
    高数_第1章空间解析几何与向量代数_向量的数量积
    学习尚硅谷HTML+CSS总结
    Django模型(二)
  • 原文地址:https://blog.csdn.net/a6636656/article/details/126099971