抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

一、快速入门

1.1、Arthas简介

Arthas 是Alibaba开源的Java诊断工具。可以安装在系统所在服务器,也可以远程诊断其他服务器的服务。可以帮助开发人员或者运维人员查找问题,分析性能,bug追踪。

1.2、Arthas的快速入门

下载地址:

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

应用程序:可以自己启一个java服务

1.3、启动Arthas

1
java -jar arthas-boot.jar 

执行arthas程序的用户要和应用程序的执行用户为相同的权限。比如:应用程序使用admin用户来执行,那么arthas的启动:sudo su admin && java -jar arthas-boot.jar 或者 sudo -u admin -EH java -jar arthas-boot.jar
java -jar arthas-boot.jar -h 打印更多的信息

开始启动:

1
2
3
4
5
6
7
8
9
10
11
12
localhost:Downloads wuxuan.chai$ java -jar arthas-boot.jar 
[INFO] arthas-boot version: 3.3.3
[INFO] Process 84742 already using port 3658
[INFO] Process 84742 already using port 8563
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 84742 com.yiyi.Application
[2]: 84898 arthas-boot.jar
[3]: 1572 kafka.Kafka
[4]: 981 org.apache.zookeeper.server.quorum.QuorumPeerMain
[5]: 84726 org.codehaus.plexus.classworlds.launcher.Launcher
[6]: 83691
[7]: 84010 org.jetbrains.idea.maven.server.RemoteMavenServer36

可以看到我们通过wuxuan.chai用户启动了7个java进程(pid):

1
2
3
4
5
6
7
* [1]: 84742 com.yiyi.Application
[2]: 84898 arthas-boot.jar
[3]: 1572 kafka.Kafka
[4]: 981 org.apache.zookeeper.server.quorum.QuorumPeerMain
[5]: 84726 org.codehaus.plexus.classworlds.launcher.Launcher
[6]: 83691
[7]: 84010 org.jetbrains.idea.maven.server.RemoteMavenServer36

选择应用的java进程:
这里我们选择我们的测试app,1
输入1,再回车/enter,arthas会attach到目标进程:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
localhost:Downloads wuxuan.chai$ java -jar arthas-boot.jar 
[INFO] arthas-boot version: 3.3.3
[INFO] Process 84742 already using port 3658
[INFO] Process 84742 already using port 8563
[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1. Then hit ENTER.
* [1]: 84742 com.yiyi.Application
[2]: 84898 arthas-boot.jar
[3]: 1572 kafka.Kafka
[4]: 981 org.apache.zookeeper.server.quorum.QuorumPeerMain
[5]: 84726 org.codehaus.plexus.classworlds.launcher.Launcher
[6]: 83691
[7]: 84010 org.jetbrains.idea.maven.server.RemoteMavenServer36
1
[INFO] arthas home: /Users/wuxuan.chai/.arthas/lib/3.3.6/arthas
[INFO] The target process already listen port 3658, skip attach.
[INFO] arthas-client connect 127.0.0.1 3658
,---. ,------. ,--------.,--. ,--. ,---. ,---.
/ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'


wiki https://alibaba.github.io/arthas
tutorials https://alibaba.github.io/arthas/arthas-tutorials
version 3.3.6
pid 84742
time 2020-07-06 10:46:47

[arthas@84742]$



1.4、Arthas的基本操作

Arthas的入门基本操作:Dashboard/thread/jad/watch,来查看一些最基本的指标信息,比如,资源占用,线程,类信息,方法执行信息。

1.4.1、查看Dashboard

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
[arthas@84742]$ dashboard 
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTED DAEMON
149 Timer-for-arthas-dashboard-3bf90e35- system 10 RUNNABLE 51 0:0 false true
113 Catalina-utility-1 main 1 TIMED_WAITI 14 0:0 false false
116 http-nio-7070-BlockPoller main 5 RUNNABLE 14 0:0 false true
127 http-nio-7070-ClientPoller main 5 RUNNABLE 13 0:0 false true
114 Catalina-utility-2 main 1 WAITING 5 0:0 false false
131 Attach Listener system 9 RUNNABLE 0 0:0 false true
130 DestroyJavaVM main 5 RUNNABLE 0 0:1 false false
3 Finalizer system 8 WAITING 0 0:0 false true
2 Reference Handler system 10 WAITING 0 0:0 false true
4 Signal Dispatcher system 9 RUNNABLE 0 0:0 false true
136 arthas-shell-server system 9 TIMED_WAITI 0 0:0 false true
137 arthas-shell-server system 9 TIMED_WAITI 0 0:0 false true
133 arthas-timer system 9 WAITING 0 0:0 false true
141 as-command-execute-daemon system 10 TIMED_WAITI 0 0:0 false true
115 container-0 main 5 TIMED_WAITI 0 0:0 false false
Memory used total max usage GC
heap 169M 370M 3641M 4.66% gc.ps_scavenge.count 4
ps_eden_space 140M 183M 1344M 10.46% gc.ps_scavenge.time(ms) 37
ps_survivor_space 0K 10752K 10752K 0.00% gc.ps_marksweep.count 2
ps_old_gen 29M 176M 2731M 1.07% gc.ps_marksweep.time(ms) 118
nonheap 53M 57M -1 92.98%
code_cache 5M 7M 240M 2.17%
metaspace 42M 44M -1 95.78%
Runtime
os.name Mac OS X
os.version 10.14.6
java.version 1.8.0_241
java.home /Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre
systemload.average 3.25
processors 8
uptime

上半部分是程序的线程信息,中间是内存使用情况,这里可以查看gc的次数,下面是当前程序运行的环境信息

1.4.2、thread命令查看进程

1
2
3
4
5
6
7
[arthas@84742]$ thread 115 
"container-0" Id=115 TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at org.apache.catalina.core.StandardServer.await(StandardServer.java:570)
at org.springframework.boot.web.embedded.tomcat.TomcatWebServer$1.run(TomcatWebServer.java:197)

Affect(row-cnt:0) cost in 1 ms.

查看线程的具体信息,调用对象,状态等。。。

1.4.3、jad反编译class类

通过jad反编译springboot的启动类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[arthas@84742]$ jad com.yiyi.Application

ClassLoader:
+-sun.misc.Launcher$AppClassLoader@4e25154f
+-sun.misc.Launcher$ExtClassLoader@2ef1e4fa

Location:
/Users/wuxuan.chai/Documents/project/springboot-learn/weixin/target/classes/

/*
* Decompiled with CFR.
*/
package com.yiyi;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;

@SpringBootApplication
@ComponentScan(value={"com.yiyi"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

Affect(row-cnt:2) cost in 181 ms.
[arthas@84742]$

1.4.4、watch查看函数的返回值

通过watch命令查看方法的返回值,查看获取微信的accessToken的方法返回值:

1
2
3
4
[arthas@84742]$ watch com.yiyi.api.WeixinSupportApi getAccessToken 
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 38 ms, listenerId: 3

执行命令后,Arthas会监听该方法的执行,每次调用,都会将执行结果打印出来,例如:

1
2
3
4
5
ts=2020-07-06 11:26:17; [cost=3.13107ms] result=@ArrayList[
@Object[][isEmpty=false;size=2],
@WeixinSupportApi[com.yiyi.api.WeixinSupportApi@14cb7037],
@String[i9c7LCIqY8cJuneYN9C9q4kN-FZoo7YZFVxEpgT3ugMmeX1KJ5HuVgSLK5IkJW7JF_adbkmjGrGdu9aw9XFhrMBLBMC9eFm82ICpst1-UVGujAvdUMcPRkUNcgb7I7MmxHk2ZVIpZzLKxf-3zw1-oE-wfhETKjRrjRhoP7z7g9UD4OS-cctNJBUJk8XfcE2cW_S2GUuj-tm2vyvVNK9oDQ],
]

二、进阶使用

2.1、基础命令

2.1.1、所有命令的集合

NAME DESCRIPTION
help 展示Arthas帮助
keymap 展示所有指定的连接的可用的快捷键
sc 搜索所有被jvm加载类
sm 搜索所有被jvm加载的类的方法
classloader 展示类加载器的信息
jad 类反编译
getstatic 展示一个类的静态的属性字段
monitor 监控方法执行的数据,例如:合计/成功/失败的数值,实时平均值失败比率等
stack 展示指定类和方法的调用栈信息
thread 展示线程信息和调用栈
trace 指定方法调用栈的执行时间
watch 展示指定方法的调用过程中,输入/输出的参数,结果返回值,异常抛出等
tt 时间隧道
jvm 展示目标jvm的信息
perfcounter 展示性能计数器的信息
ognl 执行ognl表达式
mc 内存编译器,将java文件在内存中编译成字节码和内存中的类文件
redefine 重新定义类,参考Instrumentation#redefineClasses(ClassDefinition…)
dashboard 预览目标jvm的线程,gc,环境变量,tomcat信息
dump 从jvm中转存类的字节码
heapdump 堆转存
options 查看和更改各种Arthas选项
cls 清理屏幕
reset 重置所有的增强类
version 展示Arthas版本
session 展示当前会话的信息
sysprop 展示,修改系统的配置
sysenv 展示系统环境变量
vmoption 展示修改vm诊断选项
logger 打印日志信息,修改日志级别
history 展示历史命令
cat 连接并打印文件内容,类似linux的cat命令
echo 将参数写入标准输出
pwd 返回工作目录名称
mbean 显示mbean信息
grep 用于管道的grep命令
tee 管道的tee命令
profiler 异步分析工具https://github.com/jvm-profiling-tools/async-profiler
stop 停止/关闭Arthas服务器并退出控制台

2.1.2、redefine命令,替换指定源class文件

redefine后的原来的类不能恢复,redefine也有可能失败(比如新增了field),reset命令对redefine无效,如果想重置,需要redefine原始的字节码。
不允许新增加field/method。
正在执行的函数,没有退出不能生效。

示例,替换接口的返回值:

1
2
3
4
5
6
7
8
9
@RestController
@RequestMapping(value = "/weixin", produces = MediaType.APPLICATION_JSON_VALUE)
@Slf4j
public class WeixinSupportApi {
@GetMapping("/test/")
public String test() {
return "这是正牌的接口结果";
}
}

请求接口会返回一个字符串:“这是正牌的接口结果”。接下来通过arthas的redifine命令替换成接口返回成一个:“hello”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
@RequestMapping(value = "/weixin", produces = MediaType.APPLICATION_JSON_VALUE)
@Slf4j
public class WeixinSupportApi {
@GetMapping("/test/")
public String test() {
return "hello";
}
}
```
方式一、
将上述的java文件编译成class文件,上传到服务器,使用redefine命令,开始替换:
```shell
redefine weixin/WeixinSupportApi.class

方式二、

1
2
3
4
5
6
7
8
9
10
11
12
13
//通过jad将class 文件编译成java文件
jad --source-only com.yiyi.api.WeixinSupportApi > weixin/src/main/java/com/yiyi/api/WeixinSupportApi.java
//通过vim对文件进行编辑,实现我们想要的一个逻辑
vim weixin/src/main/java/com/yiyi/api/WeixinSupportApi.java
//通过mc 内存编译,将上面的java文件编译成class 代码
mc weixin/src/main/java/com/yiyi/api/WeixinSupportApi.java -d weixin
->输出编译后的class 文件地址:
Memory compiler output:
/Users/wuxuan.chai/Documents/project/springboot-learn/weixin/com/yiyi/api/WeixinSupportApi.class
Affect(row-cnt:1) cost in 1467 ms.
//redefine 替换内存中的class文件为刚才编译的class 文件
redefine /Users/wuxuan.chai/Documents/project/springboot-learn/weixin/com/yiyi/api/WeixinSupportApi.class
即可实现对源class 的编辑,并且进行热部署。。

2.1.3、headdump命令

dump java heap,类似jmap命令的heap dump功能

1
2
3
4
5
6
//dump到指定的文件
heapdump ./dump.hprof
//只dump live对象
heapdump --live ./dump.hprof
//dump到临时文件
heapdump

2.1.4、classloader的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//按类加载类型查看统计信息
[arthas@89977]$ classloader
name numberOfInstances loadedCountTotal
sun.misc.Launcher$AppClassLoader 1 4440
BootstrapClassLoader 1 2932
com.taobao.arthas.agent.ArthasClassloader 1 2095
lombok.launch.ShadowClassLoader 1 242
sun.reflect.DelegatingClassLoader 77 77
sun.misc.Launcher$ExtClassLoader 1 4
javax.management.remote.rmi.NoCallStackClassLoader 2 2
sun.reflect.misc.MethodUtil 1 1
Affect(row-cnt:8) cost in 5 ms.

//按类加载实例查看统计信息
[arthas@89977]$ classloader -l
name loadedCount hash parent
BootstrapClassLoader 2932 null null
com.taobao.arthas.agent.ArthasClassloader@3c708a1d 2095 3c708a1d sun.misc.Launcher$ExtClassLoader@770c2e6b
javax.management.remote.rmi.NoCallStackClassLoader@1060b431 1 1060b431 null
javax.management.remote.rmi.NoCallStackClassLoader@63440df3 1 63440df3 null
lombok.launch.ShadowClassLoader@1756d9eb 242 1756d9eb sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$AppClassLoader@18b4aac2 4440 18b4aac2 sun.misc.Launcher$ExtClassLoader@770c2e6b
sun.misc.Launcher$ExtClassLoader@770c2e6b 4 770c2e6b null
sun.reflect.misc.MethodUtil@2c109df9 1 2c109df9 sun.misc.Launcher$AppClassLoader@18b4aac2
Affect(row-cnt:8) cost in 5 ms.

// 查看classloader的继承树
[arthas@89977]$ classloader -t
+-BootstrapClassLoader
+-javax.management.remote.rmi.NoCallStackClassLoader@1060b431
+-javax.management.remote.rmi.NoCallStackClassLoader@63440df3
+-sun.misc.Launcher$ExtClassLoader@770c2e6b
+-com.taobao.arthas.agent.ArthasClassloader@3c708a1d
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-lombok.launch.ShadowClassLoader@1756d9eb
+-sun.reflect.misc.MethodUtil@2c109df9
Affect(row-cnt:8) cost in 5 ms.

//查看URLClassloader的实际urls
[arthas@89977]$ classloader -c 770c2e6b
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/sunec.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/nashorn.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/cldrdata.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/jfxrt.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/dnsns.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/localedata.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/bcprov-jdk16-143.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/bcprov-jdk15-135.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/jaccess.jar
file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/zipfs.jar
file:/System/Library/Java/Extensions/MRJToolkit.jar
Affect(row-cnt:84) cost in 5 ms.

//尝试查找类的class文件
[arthas@89977]$ classloader -c 770c2e6b -r java/lang/String.class jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/rt.jar!/java/lang/String.class

Affect(row-cnt:1) cost in 4 ms.

//使用classloader加载类
[arthas@89977]$ classloader -c 2c109df9 --load com.yiyi.api.WeixinSupportApi
load class success.
class-info com.yiyi.api.WeixinSupportApi
code-source /Users/wuxuan.chai/Documents/project/springboot-learn/weixin/target/classes/
name com.yiyi.api.WeixinSupportApi
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name WeixinSupportApi
modifier public
annotation org.springframework.web.bind.annotation.RestController,org.springframework.web.bind.annotation.RequestMapping
interfaces
super-class +-java.lang.Object
class-loader +-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@770c2e6b
classLoaderHash 18b4aac2


//使用classloader去查找resource
[arthas@89977]$ classloader -c 2c109df9 -r META-INF/MANIFEST.MF
jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/bcprov-jdk16-143.jar!/META-INF/MANIFEST.MF
jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/bcprov-jdk15-135.jar!/META-INF/MANIFEST.MF
jar:file:/System/Library/Java/Extensions/MRJToolkit.jar!/META-INF/MANIFEST.MF
jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/charsets.jar!/META-INF/MANIFEST.MF
jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/deploy.jar!/META-INF/MANIFEST.MF
jar:file:/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/jre/lib/ext/bcprov-jdk15-135.jar!/META-INF/MANIFEST.MF

2.1.4、getstatic获取类中的静态属性

1
2
3
4
[arthas@89977]$ getstatic com.yiyi.api.WeixinSupportApi CORP_ID
field: CORP_ID
@String[ww91156f2c9067a7ba]
Affect(row-cnt:1) cost in 4 ms.

如果对象是一个复杂的对象,可以使用OGNL表达式进行遍历,过滤

2.1.5、tt命令,记录调用记录

方法执行记录的隧道,记录指定方法每次调用的入参和返回的信息,并能对这些不同时间下的调用进行观测。

tt的调用方式:
|选项|描述|
|-|-|
|-d,–delete|删除索引指定的时间片段|
|–delete-all|删除所有的时间片段|
|-x,–expand value|展开对象级别(默认为1)|
|-h,–help|命令使用指南|
|-i,–index value|展示指定的时间片段的相信信息|
|-n,–limits value|执行时间阈值|
|-l,–list|展示所有的时间片段的信息|
|–listenerId value|指定监听id|
|-p,–play|重放由索引指定的时间片段|
|-E,–regex|启用正则表达式匹配(默认情况下为通配符匹配)|
|–replay-interval value|选项r大于1的tt的回放间隔|
|–replay-times value|执行tt时的次数|
|-s,–search-express value|搜索表达式,用ognl表达式搜索时间片段。|
||advice的结构类似条件式表达|
|-M,–sizeLimit value|结果的字节上限(默认为1010241024)|
|-t,–time-tunnel|在时间片段内记录方法调用|
|-w,–watch-express value>|观看ognlexpress的时间片段|
|class-pattern|模式匹配的路径和类名|
|method-pattern|模式匹配法|
|condition-express|ognl风格的条件表达式|

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//记录调用
[arthas@89977]$ tt -t com.yiyi.api.WeixinSupportApi test
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 129 ms, listenerId: 1
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
1000 2020-07-06 17:21:20 0.531715 true false 0x437d2cf WeixinSupportApi test
1001 2020-07-06 17:21:24 0.026004 true false 0x437d2cf WeixinSupportApi test
1002 2020-07-06 17:21:27 0.027567 true false 0x437d2cf WeixinSupportApi test
1003 2020-07-06 17:21:28 0.064779 true false 0x437d2cf WeixinSupportApi test

//展示时间片列表
[arthas@89977]$ tt -l
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
1000 2020-07-06 17:21:20 0.531715 true false 0x437d2cf WeixinSupportApi test
1001 2020-07-06 17:21:24 0.026004 true false 0x437d2cf WeixinSupportApi test
1002 2020-07-06 17:21:27 0.027567 true false 0x437d2cf WeixinSupportApi test
1003 2020-07-06 17:21:28 0.064779 true false 0x437d2cf WeixinSupportApi test
Affect(row-cnt:4) cost in 1 ms.


//展示时间片的具体信息
[arthas@89977]$ tt -i 1000
INDEX 1000
GMT-CREATE 2020-07-06 17:21:20
COST(ms) 0.531715
OBJECT 0x437d2cf
CLASS com.yiyi.api.WeixinSupportApi
METHOD test
IS-RETURN true
IS-EXCEPTION false
RETURN-OBJ @String[hello]
Affect(row-cnt:1) cost in 1 ms.


//重新调用索引为1000的调用
[arthas@89977]$ tt -i 1000 -p
RE-INDEX 1000
GMT-REPLAY 2020-07-06 17:22:10
OBJECT 0x437d2cf
CLASS com.yiyi.api.WeixinSupportApi
METHOD test
IS-RETURN true
IS-EXCEPTION false
COST(ms) 0.260785
RETURN-OBJ @String[hello]
Time fragment[1000] successfully replayed 1 times.
[arthas@89977]$
表格字段 字段解释
INDEX 时间片段记录编号,每一个编号代表着一次调用,后续tt还有很多命令都是基于此编号指定记录操作,非常重要。
TIMESTAMP 方法执行的本机时间,记录了这个时间片段所发生的本机时间
COST(ms) 方法执行的耗时
IS-RET 方法是否以正常返回的形式结束
IS-EXP 方法是否以抛异常的形式结束
OBJECT 执行对象的hashCode(),注意,曾经有人误认为是对象在JVM中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体
CLASS 执行的类名
METHOD 执行的方法名

使用OGNL表达式,对调用参数进行过滤监听。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
[arthas@89977]$ tt -t com.yiyi.api.WeixinSupportApi sendMessage 'params[0].text.content=="test"'
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 72 ms, listenerId: 5
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
1004 2020-07-06 17:33:42 354.578854 true false 0x437d2cf WeixinSupportApi sendMessage
[arthas@89977]$ tt -i 1004
INDEX 1004
GMT-CREATE 2020-07-06 17:33:42
COST(ms) 354.578854
OBJECT 0x437d2cf
CLASS com.yiyi.api.WeixinSupportApi
METHOD sendMessage
IS-RETURN true
IS-EXCEPTION false
PARAMETERS[0] @SendMessageVO[
touser=@String[Feng],
msgtype=@String[text],
agentid=@Integer[1000036],
text=@Text[com.yiyi.model.SendMessageVO$Text@504511d7],
safe=@Integer[0],
]
RETURN-OBJ @WeixinResponse[
errcode=@Integer[0],
errmsg=@String[ok],
invaliduser=@String[],
]
Affect(row-cnt:1) cost in 1 ms.

2.1.6、使用sc查看jvm已加载的类信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
//模糊匹配,查看某个包下面的类加载信息
[arthas@89977]$ sc com.yiyi.*
com.yiyi.Application
com.yiyi.Application$$EnhancerBySpringCGLIB$$7f85004e
com.yiyi.WebConfig
com.yiyi.WebConfig$$EnhancerBySpringCGLIB$$bf76c934
com.yiyi.api.WeixinSupportApi
com.yiyi.api.WeixinSupportApi$$Lambda$451/966754950
com.yiyi.api.WeixinSupportApi$$Lambda$467/1545239474
com.yiyi.api.WeixinSupportApi$$Lambda$468/1304820993
com.yiyi.model.AccessTokenVo
com.yiyi.model.SendMessageVO
com.yiyi.model.SendMessageVO$Text
com.yiyi.model.WeixinResponse
Affect(row-cnt:12) cost in 15 ms.


//指定类的详细信息
[arthas@89977]$ sc -d com.yiyi.api.WeixinSupportApi
class-info com.yiyi.api.WeixinSupportApi
code-source /Users/wuxuan.chai/Documents/project/springboot-learn/weixin/target/classes/
name com.yiyi.api.WeixinSupportApi
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name WeixinSupportApi
modifier public
annotation org.springframework.web.bind.annotation.RestController,org.springframework.web.bind.annotation.RequestMapping
interfaces
super-class +-java.lang.Object
class-loader +-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@770c2e6b
classLoaderHash 18b4aac2

Affect(row-cnt:1) cost in 8 ms.

// 指定类的加载信息以及属性字段的信息
[arthas@89977]$ sc -d -f com.yiyi.api.WeixinSupportApi
class-info com.yiyi.api.WeixinSupportApi
code-source /Users/wuxuan.chai/Documents/project/springboot-learn/weixin/target/classes/
name com.yiyi.api.WeixinSupportApi
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name WeixinSupportApi
modifier public
annotation org.springframework.web.bind.annotation.RestController,org.springframework.web.bind.annotation.RequestMapping
interfaces
super-class +-java.lang.Object
class-loader +-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@770c2e6b
classLoaderHash 18b4aac2
fields name log
type org.slf4j.Logger
modifier final,private,static
value Logger[com.yiyi.api.WeixinSupportApi]

name response
type javax.servlet.http.HttpServletResponse
modifier private
annotation javax.annotation.Resource

name CORP_ID
type java.lang.String
modifier final,private,static
value ww91156f2c9067a7ba

name APP_SECRET
type java.lang.String
modifier final,private,static
value r5ifq9RFqtsL34sdWXmrzMytY7KYksz5cAimzoH1jeg

name ACCESS_TOKEN_CACHE
type com.google.common.cache.Cache
modifier final,private,static
value com.google.common.cache.LocalCache$LocalManualCache@39373908


Affect(row-cnt:1) cost in 10 ms.

2.1.7、mc,内存编译

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

1
mc /tmp/Test.java

可以通过-c指定classloader:

1
mc -c 327a647b /tmp/Test.java

可以通过-d指定输出的class文件的目录

1
mc -d /tmp/output /tmp/ClassA.java /tmp/ClassB.java

编译成的.class文件后,可以结合redefine命令实现热更新代码

注意,mc命令有可能失败。如果编译失败可以在本地编译好.class文件,再上传到服务器。

2.1.8、查看方法的stack

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
[arthas@89977]$ stack com.yiyi.api.WeixinSupportApi getAccessToken 
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 43 ms, listenerId: 8
ts=2020-07-06 17:50:49;thread_name=http-nio-7070-exec-2;id=83;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@2fc07784
@com.yiyi.api.WeixinSupportApi.getAccessToken()
at com.yiyi.api.WeixinSupportApi.sendMessage(WeixinSupportApi.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
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:879)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:793)
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:660)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:741)
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.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:1590)
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)

也可以使用OGNL表达式,对请求参数进行过滤,或者使用执行时间进行过滤

2.2.1、Springboot集成arthas

引入arthas的依赖

1
2
3
4
5
6
7
8
9
10
<properties>
<arthas.version>3.3.6</arthas.version>
</properties>
<dependencies>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-spring-boot-starter</artifactId>
<version>${arthas.version}</version>
</dependency>
</dependencies>

应用配置:application.properties

1
2
3
4
//指定arthas的agentId
arthas.agentId=weixin
//arthas tunnel server 的websocket的地址
arthas.tunnel-server=ws://localhost:7777/ws

重启应用

2.2.2、通过浏览器连接arthas

第一步:下载arthas tunnel server
wget https://github.com/alibaba/arthas/releases/download/arthas-all-3.3.6/arthas-tunnel-server-3.3.6.jar
第二步:启动arthas tunnel server

1
java -jar arthas-tunnel-server.jar -server.port=8888 启动并指定web的端口

这个服务有两个socket连接方式,一个正常的http,用的端口是我们指定的8888,还有一个就是供显示服务器的命令行的websocket连接,端口7777
第三步:
访问arthas tunnel server ,我是本机测试,所以就是localhost:8888,如图:

首页

连接指定的应用:
连接成功

默认情况下,连接地址为本地,websocket的端口为7777

三、总结

Arthas贵为java应用诊断神器,对于应用的资源占用,性能分析,jvm监控都有很大的支持帮助。除此之外,在排查线上问题时,可以监听对应的方法的调用,以及动态修改class类,很湿,很方便。而且Arthas对docker的java进程,以及k8s都有很好的支持,后续用到的时候在做学习。

评论