一、基本简介
Arthas是阿里巴巴开源的一款Java线上诊断工具。
二、基本使用
0、快速安装
# 下载jar包
curl -O https://arthas.aliyun.com/arthas-boot.jar
# 运行jar包
java -jar arthas-boot.jar
启动上述jar包后,控制台会给出与执行该jar包用户权限一致的Java进程列表,供选择,然后在控制台输入列表前方的序号,Arthas就会依附到选中的目标进程上,然后就可以在控制台通过Arthas提供的命令进行相关操作。
Arthas全部命令请查看官网。
1、getstatic命令
该命令用于获取类的静态属性。
[arthas@17204]$ getstatic java.lang.String serialVersionUID
field: serialVersionUID
@Long[-6849794470754667710]
上述命令用于获取String类的静态属性serialVersionUID的值,返回结果显示为-6849794470754667710。
2、sc命令
该命令用于搜索并查看JVM已加载的类信息,且支持模糊搜索和正则匹配。常用参数:-d(输出当前类的详细信息)、-f(输出当前类的成员变量信息,需要和-d一起使用)、-E(正则匹配)。
[arthas@17204]$ sc -d *MdConstants
class-info com.lyentech.bdc.staff.md.MdConstants
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(正则匹配)。
[arthas@17204]$ sm -d org.apache.commons.lang.StringUtils trim
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(正则匹配)。
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命令获取)。
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命令获取)。
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命令获取)。
# 获取String类的静态属性
[arthas@17204]$ ognl '@java.lang.String@serialVersionUID'
@Long[-6849794470754667710]
# 调用指定类的静态方法
[arthas@17204]$ ognl -c 49c2faae '@com.lyentech.bdc.staff.utils.MysqlQueryUtil@handleSpecialChar("haha")'
@String[haha]
# 执行多行表达式,赋值给临时变量,返回一个List
[arthas@17204]$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
@ArrayList[
@String[/mnt/jdk1.8.0_231/jre],
@String[Java(TM) SE Runtime Environment],
]
8、watch命令
该命令用于观察指定方法的调用情况,能观察到的范围:入参、返回值、异常,可通过编写ognl表达式进行对应变量的查看。常用参数:-b(方法调用前)、-e(方法异常后)、-s(方法返回后)、-f(方法结束后)、-x(输出结果的属性遍历深度)、-n(执行次数限制)
[arthas@17204]$ watch com.lyentech.bdc.staff.controller.StaffController getUserList "{params,returnObj}" -x 3 -n 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 80 ms, listenerId: 2
ts=2020-09-02 11:13:10; [cost=11.924017ms] result=@ArrayList[
@Object[][
@SearchStaffParam[
username=null,
phone=null,
pageNum=@Long[1],
pageSize=@Long[10],
arkOrgList=@ArrayList[isEmpty=false;size=1],
],
],
@ResultEntity[
code=@Integer[1000],
msg=@String[请求成功],
data=@PageResult[
pageNum=@Long[1],
pageSize=@Long[10],
totalPage=@Long[1],
totalCount=@Long[6],
pageList=@ArrayList[isEmpty=false;size=6],
],
],
]
注:
- params代表入参对象
- target代表当前观察方法所对应的对象
- returnObj代表返回对象
- throwExp代表抛出的异常对象
9、trace命令
该命令用于输出方法的调用路径,并统计整个调用链上的性能开销及每个节点上的耗时。常用参数:-n(执行次数限制)。
[arthas@17204]$ trace com.lyentech.bdc.staff.controller.StaffController getUserList -n 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 229 ms, listenerId: 5
`---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
`---[10.709124ms] com.lyentech.bdc.staff.controller.StaffController:getUserList()
+---[0.021961ms] com.lyentech.bdc.staff.model.param.SearchStaffParam:verify() #49
+---[10.531731ms] com.lyentech.bdc.staff.service.apollo.StaffService:getStaffList() #50
`---[0.043171ms] com.lyentech.bdc.http.response.ResultEntity:success() #50
10、stack命令
该命令用于输出指定方法的调用路径,常用参数:-n(执行次数限制)。
[arthas@17204]$ stack com.lyentech.bdc.staff.controller.StaffController getUserList -n 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 74 ms, listenerId: 8
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
@com.lyentech.bdc.staff.controller.StaffController.getUserList()
at sun.reflect.GeneratedMethodAccessor142.invoke(null:-1)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:190)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:138)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:105)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1040)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:943)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:909)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:652)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:88)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:114)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:104)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:202)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1589)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
11、tt命令
该命令可以记录下指定方法的每次调用环境现场,然后可以针对某次调用进行其他操作。常用参数:-t(记录方法的调用现场)、-n(执行次数限制)、-l(查看记录列表)、-s(从列表中搜索,后接表达式)、-i(查看某次调用的详细信息,后接index编号)、-w(实现对调用现场的二级操作,通过ognl表达式)、-d(删除某次调用记录,后接index编号)、—delete-all(删除所有调用记录)、-x(输出结果的属性遍历深度)。
# 记录一次RequestMappingHandlerAdapter中invokeHandlerMethod的调用
[arthas@17204]$ tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod -n 1
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 97 ms, listenerId: 12
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
-----------------------------------------------------------------------------------------------------------
1000 2020-09-02 14:05:41 26.127921 true false 0x43533dbe RequestMappingHandlerAdapter invokeHandlerMethod
# 查看调用记录的详情
[arthas@17204]$ tt -i 1000
# 针对某次调用现场进行操作,先获取SpringContext,再通过getBean方法通过名称获取指定的Bean
[arthas@17204]$ tt -i 1000 -w 'target.getApplicationContext().getBean("mdConstants")'
@MdConstants[
ZEUS=@Integer[1],
HERMES=@Integer[2],
VENUS=@Integer[3],
ARK_ORG_TYPE=@[isEmpty=false;size=3],
zeusKey=@String[4baecd420612ce60edcd817b42768f3b],
zeusSecret=@String[7486b7ba63300f60],
hermesKey=@String[a6b11312de32454d68398a6ee8819963],
hermesSecret=@String[77298527cec0864f],
venusKey=@String[598c15997bb83d231439a3d3e786e46d],
venusSecret=@String[ecde3176a0dedbf6],
]
# 调用某个Bean的普通方法
[arthas@17204]$ tt -i 1000 -w 'target.getApplicationContext().getBean("mdConstants").getKey(1)'
@String[4baecd420612ce60edcd817b42768f3b]
12、dashboard命令
该命令用于查看当前系统的实时数据面板。输出如下:
第一部分是线程相关的数据展示。包括线程ID、NAME、GROUP、优先级、状态、消耗的CPU占比、运行总时间、当前的中断位状态、是否是daemon线程。
第二部分是内存相关的数据展示。包括堆内存和非堆内存的大小,及其内部分类的各自大小;GC的次数和时间。
第三部分是当前运行环境的数据展示。包括OS名称及版本、Jdk版本、运行时长等。
13、jvm命令
该命令用于查看当前JVM的相关信息。
14、sysprop命令
该命令用于查看当前JVM的系统属性(对应System.getProperties()),也可以修改单个属性。
15、sysenv命令
该命令用于查看当前JVM的环境属性(对应System.getenv())。