一、基本简介

Arthas是阿里巴巴开源的一款Java线上诊断工具。

二、基本使用

0、快速安装

  1. # 下载jar包
  2. curl -O https://arthas.aliyun.com/arthas-boot.jar
  3. # 运行jar包
  4. java -jar arthas-boot.jar

启动上述jar包后,控制台会给出与执行该jar包用户权限一致的Java进程列表,供选择,然后在控制台输入列表前方的序号,Arthas就会依附到选中的目标进程上,然后就可以在控制台通过Arthas提供的命令进行相关操作。

Arthas全部命令请查看官网

1、getstatic命令

该命令用于获取类的静态属性。

  1. [arthas@17204]$ getstatic java.lang.String serialVersionUID
  2. field: serialVersionUID
  3. @Long[-6849794470754667710]

上述命令用于获取String类的静态属性serialVersionUID的值,返回结果显示为-6849794470754667710。

2、sc命令

该命令用于搜索并查看JVM已加载的类信息,且支持模糊搜索和正则匹配。常用参数:-d(输出当前类的详细信息)、-f(输出当前类的成员变量信息,需要和-d一起使用)、-E(正则匹配)。

  1. [arthas@17204]$ sc -d *MdConstants
  2. class-info com.lyentech.bdc.staff.md.MdConstants
  3. code-source file:/opt/steward-backend/staff-backend-test.jar!/BOOT-INF/classes!/ name com.lyentech.bdc.staff.md.MdConstants isInterface false isAnnotation false isEnum false isAnonymousClass false isArray false isLocalClass false isMemberClass false isPrimitive false isSynthetic false simple-name MdConstants modifier public annotation org.springframework.stereotype.Component interfaces super-class +-java.lang.Object class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@49c2faae +-sun.misc.Launcher$AppClassLoader@70dea4e +-sun.misc.Launcher$ExtClassLoader@303a7070 classLoaderHash 49c2faae

3、sm命令

该命令用于查看当前指定类所声明的方法信息,无法看到父类的信息。常用参数:-d(展示方法详情)、-E(正则匹配)。

  1. [arthas@17204]$ sm -d org.apache.commons.lang.StringUtils trim
  2. declaring-class org.apache.commons.lang.StringUtils method-name trim modifier public,static annotation parameters java.lang.String return java.lang.String exceptions classLoaderHash 49c2faae

4、jad命令

该命令用于将JVM中实际运行的class文件反编译成java代码,也可以反编译指定的函数。常用参数:-c(指定类所属ClassLoader的hashcode,可以通过sc命令获取)、—source-only(只显示源码,不显示ClassLoader信息)、-E(正则匹配)。

  1. jad --source-only java.lang.String > /opt/arthas-3.3.9/tmp/String.java

上述命令反编译JDK的String类,仅输出源码,并将源码保存到/opt/arthas-3.3.9/tmp/String.java文件中。

5、mc命令

该命令用于将java文件编译成class文件,使用CFR编译技术(jad反编译同样使用这个技术)。常用参数:-d(将编译结果输出到指定目录)、-c(该类ClassLoader的hashcode,可通过sc命令获取)。

  1. mc -c 49c2faae /opt/arthas-3.3.9/tmp/StringUtils.java -d /opt/arthas-3.3.9/tmp/

上述命令,通过指定ClassLoader,将之前反编译的java文件重新编译并输出到目录/opt/arthas-3.3.9/tmp/下。

注:mc命令编译可能失败,如果源代码中有使用lambda表达式,则CFR技术会编译失败。

6、redefine命令

该命令用于重新加载新的class文件,实现对线上代码的热更新。常用参数:-c(指定Classloader的hashcode,可通过sc命令获取)。

  1. redefine /opt/arthas-3.3.9/tmp/org/apache/commons/lang/StringUtils.class

上述命令将重新加载指定的class文件到JVM中。

注:

  • 如果mc命令失败,可以先在本地编译好,再上传到服务器上进行热加载
  • 热加载的class不能添加、删除类的field和method,最多只是修改方法内的代码逻辑
  • 正在跑的函数,没有退出不会生效(比如在死循环中添加代码就不会生效)

7、ognl命令

该命令用于执行ognl表达式,可用来获取类的静态属性、调用类的静态方法等等。常用参数:-c(指定类ClassLoader的hashcode,可通过sc命令获取)。

  1. # 获取String类的静态属性
  2. [arthas@17204]$ ognl '@java.lang.String@serialVersionUID'
  3. @Long[-6849794470754667710]
  4. # 调用指定类的静态方法
  5. [arthas@17204]$ ognl -c 49c2faae '@com.lyentech.bdc.staff.utils.MysqlQueryUtil@handleSpecialChar("haha")'
  6. @String[haha]
  7. # 执行多行表达式,赋值给临时变量,返回一个List
  8. [arthas@17204]$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
  9. @ArrayList[
  10. @String[/mnt/jdk1.8.0_231/jre],
  11. @String[Java(TM) SE Runtime Environment],
  12. ]

8、watch命令

该命令用于观察指定方法的调用情况,能观察到的范围:入参、返回值、异常,可通过编写ognl表达式进行对应变量的查看。常用参数:-b(方法调用前)、-e(方法异常后)、-s(方法返回后)、-f(方法结束后)、-x(输出结果的属性遍历深度)、-n(执行次数限制)

  1. [arthas@17204]$ watch com.lyentech.bdc.staff.controller.StaffController getUserList "{params,returnObj}" -x 3 -n 1
  2. Press Q or Ctrl+C to abort.
  3. Affect(class count: 1 , method count: 1) cost in 80 ms, listenerId: 2
  4. ts=2020-09-02 11:13:10; [cost=11.924017ms] result=@ArrayList[
  5. @Object[][
  6. @SearchStaffParam[
  7. username=null,
  8. phone=null,
  9. pageNum=@Long[1],
  10. pageSize=@Long[10],
  11. arkOrgList=@ArrayList[isEmpty=false;size=1],
  12. ],
  13. ],
  14. @ResultEntity[
  15. code=@Integer[1000],
  16. msg=@String[请求成功],
  17. data=@PageResult[
  18. pageNum=@Long[1],
  19. pageSize=@Long[10],
  20. totalPage=@Long[1],
  21. totalCount=@Long[6],
  22. pageList=@ArrayList[isEmpty=false;size=6],
  23. ],
  24. ],
  25. ]

注:

  • params代表入参对象
  • target代表当前观察方法所对应的对象
  • returnObj代表返回对象
  • throwExp代表抛出的异常对象

9、trace命令

该命令用于输出方法的调用路径,并统计整个调用链上的性能开销及每个节点上的耗时。常用参数:-n(执行次数限制)。

  1. [arthas@17204]$ trace com.lyentech.bdc.staff.controller.StaffController getUserList -n 1
  2. Press Q or Ctrl+C to abort.
  3. Affect(class count: 1 , method count: 1) cost in 229 ms, listenerId: 5
  4. `---ts=2020-09-02 11:37:57;thread_name=http-nio-33334-exec-10;id=2e;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@389b0789
  5. `---[10.709124ms] com.lyentech.bdc.staff.controller.StaffController:getUserList()
  6. +---[0.021961ms] com.lyentech.bdc.staff.model.param.SearchStaffParam:verify() #49
  7. +---[10.531731ms] com.lyentech.bdc.staff.service.apollo.StaffService:getStaffList() #50
  8. `---[0.043171ms] com.lyentech.bdc.http.response.ResultEntity:success() #50

10、stack命令

该命令用于输出指定方法的调用路径,常用参数:-n(执行次数限制)。

  1. [arthas@17204]$ stack com.lyentech.bdc.staff.controller.StaffController getUserList -n 1
  2. Press Q or Ctrl+C to abort.
  3. Affect(class count: 1 , method count: 1) cost in 74 ms, listenerId: 8
  4. ts=2020-09-02 11:45:05;thread_name=http-nio-33334-exec-4;id=28;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@389b0789
  5. @com.lyentech.bdc.staff.controller.StaffController.getUserList()
  6. at sun.reflect.GeneratedMethodAccessor142.invoke(null:-1)
  7. at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  8. at java.lang.reflect.Method.invoke(Method.java:498)
  9. at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
  10. at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
  11. at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
  12. at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)
  13. at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
  14. at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
  15. at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
  16. at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
  17. at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
  18. at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
  19. at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
  20. at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
  21. at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
  22. at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
  23. at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  24. at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
  25. at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  26. at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  27. at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:88)
  28. at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
  29. at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  30. at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  31. at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:114)
  32. at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:104)
  33. at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
  34. at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  35. at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  36. at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
  37. at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
  38. at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
  39. at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
  40. at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
  41. at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
  42. at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
  43. at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
  44. at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
  45. at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
  46. at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
  47. at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
  48. at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
  49. at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
  50. at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589)
  51. at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
  52. at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
  53. at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
  54. at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
  55. at java.lang.Thread.run(Thread.java:748)

11、tt命令

该命令可以记录下指定方法的每次调用环境现场,然后可以针对某次调用进行其他操作。常用参数:-t(记录方法的调用现场)、-n(执行次数限制)、-l(查看记录列表)、-s(从列表中搜索,后接表达式)、-i(查看某次调用的详细信息,后接index编号)、-w(实现对调用现场的二级操作,通过ognl表达式)、-d(删除某次调用记录,后接index编号)、—delete-all(删除所有调用记录)、-x(输出结果的属性遍历深度)。

  1. # 记录一次RequestMappingHandlerAdapter中invokeHandlerMethod的调用
  2. [arthas@17204]$ tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod -n 1
  3. Press Q or Ctrl+C to abort.
  4. Affect(class count: 1 , method count: 1) cost in 97 ms, listenerId: 12
  5. INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
  6. -----------------------------------------------------------------------------------------------------------
  7. 1000 2020-09-02 14:05:41 26.127921 true false 0x43533dbe RequestMappingHandlerAdapter invokeHandlerMethod
  8. # 查看调用记录的详情
  9. [arthas@17204]$ tt -i 1000
  10. # 针对某次调用现场进行操作,先获取SpringContext,再通过getBean方法通过名称获取指定的Bean
  11. [arthas@17204]$ tt -i 1000 -w 'target.getApplicationContext().getBean("mdConstants")'
  12. @MdConstants[
  13. ZEUS=@Integer[1],
  14. HERMES=@Integer[2],
  15. VENUS=@Integer[3],
  16. ARK_ORG_TYPE=@[isEmpty=false;size=3],
  17. zeusKey=@String[4baecd420612ce60edcd817b42768f3b],
  18. zeusSecret=@String[7486b7ba63300f60],
  19. hermesKey=@String[a6b11312de32454d68398a6ee8819963],
  20. hermesSecret=@String[77298527cec0864f],
  21. venusKey=@String[598c15997bb83d231439a3d3e786e46d],
  22. venusSecret=@String[ecde3176a0dedbf6],
  23. ]
  24. # 调用某个Bean的普通方法
  25. [arthas@17204]$ tt -i 1000 -w 'target.getApplicationContext().getBean("mdConstants").getKey(1)'
  26. @String[4baecd420612ce60edcd817b42768f3b]

12、dashboard命令

该命令用于查看当前系统的实时数据面板。输出如下:

arthas - 图1

第一部分是线程相关的数据展示。包括线程ID、NAME、GROUP、优先级、状态、消耗的CPU占比、运行总时间、当前的中断位状态、是否是daemon线程。

第二部分是内存相关的数据展示。包括堆内存和非堆内存的大小,及其内部分类的各自大小;GC的次数和时间。

第三部分是当前运行环境的数据展示。包括OS名称及版本、Jdk版本、运行时长等。

13、jvm命令

该命令用于查看当前JVM的相关信息。

14、sysprop命令

该命令用于查看当前JVM的系统属性(对应System.getProperties()),也可以修改单个属性。

15、sysenv命令

该命令用于查看当前JVM的环境属性(对应System.getenv())。