![5f3939014163d606c4f8bd23cd6d4510.png](https://i-blog.csdnimg.cn/blog_migrate/6f1ab3f97cca56bcca3ab49148603219.png)
arthas中文直译过来叫阿尔萨斯,是阿里巴巴开源的java应用诊断工具,在线排查问题,无需重启服务,实时监控jvm状态。支持JDK6以上版本,支持Linux/Mac/Window。
采用命令行交换方式,tab键命令自动补全,而且兼容linux系统部分命令,如:grep,ctrl+l清屏,ctrl+a跳到行首,ctrl+c终止命令等
如果你还在使用jdk原生jstat、jmap等命令排查问题,那么,强烈建议你使用arthas。本文介绍arthas的下载安装、启动、常用命令、退出、卸载以及常见问题的调试步骤
下载安装
arthas安装分为快速安装和全量安装,快速安装只下载arthas-boot.jar。全量安装是下载一个压缩包,压缩包中包含arthas-boot.jar、测试demo和封装好的启动脚本等文件,一般我们使用快速安装就可以了
快速安装
linux系统安装可以用curl命令从阿里云官方网站下载
curl -O https://arthas.aliyun.com/arthas-boot.jar
windows系统安装用浏览器打开如下地址会提示下载
https://arthas.aliyun.com/arthas-boot.jar
全量安装
linux全量安装需要先下载执行脚本
curl -L https://arthas.aliyun.com/install.sh | sh
下载完成后会在当前目录生成as.sh脚本,执行脚本 ./as.sh 后会自动下载全量安装包,默认保存在~/.arthas/lib目录下。修改as.sh脚本可以更改默认保存路径
![3272971180c4e443e4f0b85d61e4b299.png](https://i-blog.csdnimg.cn/blog_migrate/7a99bcd62ef687ea55f430bbb9ad9d96.png)
windows全量安装只需要用浏览器打开下载地址就会提示下载
https://arthas.aliyun.com/download/latest_version?mirror=aliyun
全量安装包的文件夹名称为arthas,目录结构如下
![5ffd693b0d3e7c9e5b55387779f3d9d9.png](https://i-blog.csdnimg.cn/blog_migrate/bbbecb0145589eec16f905a06971d07d.jpeg)
全量安装包的目录结构补充一下
启动
启动之前需要明确要监听哪个java程序,并找到这个java程序的进程id。当前登录用户必须对要监听的进程有可操作权限,否则会报错
linux下可以使用ps或jps命令可以找到java程序的进程id
ps -ef | grep java jps -mlvV
windows下打开任务管理器可以找到对应程序的进程id
![db4716150675e8be0f31d729b1e4be58.png](https://i-blog.csdnimg.cn/blog_migrate/f3ad4af694be186a2b38d3bf96a0d5f9.png)
执行命令启动arthas
java -jar arthas-boot.jar
启动后会提示选择监听哪个进程id,输入对应的序号即可。我要监听的进程id是19646,也就是序号2
![427080aeb1c39571c37cb61c40d0906b.png](https://i-blog.csdnimg.cn/blog_migrate/4ab93edc946df72b71a138f69886cb8d.jpeg)
输入2之后敲回车,启动成功如下图
![b9195d2dc91f64423e94622cfd5003bf.png](https://i-blog.csdnimg.cn/blog_migrate/d07ca9720755499211e789e438a02fd5.jpeg)
启动时可以直接指定pid,就不用在启动过程中再输入序号了
java -jar arthas-boot.jar 19646
如果安装时选择了全量安装,那么也可以通过脚本启动,linux系统使用as.sh脚本,windows系统使用as.bat脚本
查看dashbord
dashboard命令可以查看cpu、线程状态,内存信息、GC情况和jdk版本等信息
![f72af7f973e9ef5ef0537489cfdb109d.png](https://i-blog.csdnimg.cn/blog_migrate/7c4f200ce498c92cb8994576be2f6720.jpeg)
图中字段解读
ID: Java中的线程ID,注意这个ID不能跟jstack中的nativeID一一对应
NAME: 线程名称
GROUP: 线程组名称
PRIORITY: 线程优先级, 1~10之间的数字,越大表示优先级越高
STATE: 线程的状态
CPU%: 线程消耗的cpu占比,采样100ms,将所有线程在这100ms内的cpu使用量求和,再算出每个线程的cpu使用占比。
TIME: 线程运行总时间,格式为 分:秒
INTERRUPTED: 线程当前的中断状态
DAEMON: 是否是守护线程
监控页面会实时刷新,默认每5000毫秒(5秒)刷新一次。可以通过 - i 参数指定刷新频率,-n 参数指定刷新次数。这个统计会有一定的开销,从截图中也可以看到arthas的cpu占比比较大,所以刷新频率不要太高,建议5秒以上,刷新次数建议10次以内
// 每10秒刷新一次,3次后停止
dashboard -i 10000 -n 3
查看线程
使用thread命令可以查看线程的状态,显示的结果实际就是dashboard结果的第一栏
![022b227fbec8431213ec7d00307d331c.png](https://i-blog.csdnimg.cn/blog_migrate/d255573b7594d7191c6a3464377ebefa.jpeg)
thread命令可以追加参数
thread命令可以追加参数
id:可以查看指定线程id的堆栈信息
-n value:找出最忙的value个线程,并打印堆栈信息
-b:找出当前正在阻塞其他线程的线程
-i value:指定采样cpu占比的时间间隔,默认为100ms。
打印线程id=24的堆栈信息
![eed4b523fbbb13e91708f2ff800ad5b5.png](https://i-blog.csdnimg.cn/blog_migrate/a70cdf12609c7a2a6cdf7e8fce9cedb4.jpeg)
打印当前最忙的3个线程的堆栈信息
![846ac1e077c57ac1f1dd31dae5556ff5.png](https://i-blog.csdnimg.cn/blog_migrate/8ab57bb546f65c0b487e12236fd7a3c0.jpeg)
打印正在阻塞其他线程的线程
![d98e7d5e7f169be4f3c4fc959827ada7.png](https://i-blog.csdnimg.cn/blog_migrate/68f14f7c918db3e3b02d30ed6b47dfcb.jpeg)
指定统计线程cpu占比的时间间隔,单位:毫秒。因为统计cpu占比会有一定的资源消耗,所以时间间隔不要太小,建议100毫秒以上
thead -i 200
反编译
使用jad可以对代码进行反编译,反编译过来的代码可能会有语法错误,但不影响阅读。比较贴心的是,反编译过来的代码语法高亮,方便阅读
![1aeb0eb1461de07007366b01ee4c99d3.png](https://i-blog.csdnimg.cn/blog_migrate/971b0db56298b3451f7cb64de321daff.jpeg)
类名支持全路径,同时也支持模糊匹配,下面两个命令可以达到相同的效果
jad com.helianxiaowu.demo.ThreadBlock
jad *ThreadBlock
也可以对类中的某一个方法进行反编译,只需要在类名后面加上方法名即可
![466d3b4babb212d4ace7d13db664a89e.png](https://i-blog.csdnimg.cn/blog_migrate/b184878bde365a24bde9c8fa278d9dde.jpeg)
反编译过来的代码默认携带ClassLoader信息,使用 --source-only 选项可以去除ClassLoader信息
![a5ce45e1109aba956027e7bdc93cb311.png](https://i-blog.csdnimg.cn/blog_migrate/6901b10c7eb1f71b209bcf8d3fed6560.png)
退出
使用quit或exit会退出arthas操作界面,但arthas进程还在,这时当你想监听别的java进程时会报错。因为arthas默认使用的是3658端口,所以这时会报错3658端口被别的进程占用。
![92523edbe98a26f1f9fe4995bdb4a4dd.png](https://i-blog.csdnimg.cn/blog_migrate/bc98aeb60db689dc50f2a50035f1843e.jpeg)
使用shutdown或stop可以结束arthas,结束后可以监听别的进程id
出现端口被占用报错时,可以再次启动进入刚才监听的进程或者使用telnet进入刚才监听的arthas进程界面,执行shutdown后stop结束监听
telnet localhost 3658
卸载
linu系统中执行如下命令进行卸载
rm -rf ~/.arthas/
rm -rf ~/logs/arthas
window下需要进入C盘当前用户的目录,找到 .arthas目录和logs/arthas目录进行删除
CPU飙升的问题调试
java程序中,频繁GC、线程数量过多导致频繁的进行切换、线程被阻塞、死循环等原因是造成cpu飙升的主要原因。
通过arthas的dashboard命令可以看到GC的回收次数和时间,现在大部分程序都是使用spring进行对象管理,不会出现创建大实例的情况。数据库链接、IO、HTTP链接都使用的链接池,也很少出现链接没有释放的问题。所以,JVM频繁进行GC操作,很有可能是内存设置不合理,可以使用如下参数指定内存
JAVA_OPTS="-server -Xms256m -Xmx512m -XX:PermSize=64M -XX:MaxPermSize=128m"
线程数量过多导致频繁进行线程切换,基本可以断定当前服务支撑不了现有的并发了,需要增加服务器资源或者增加负载
现在来模拟一个cpu飙升的问题排查过程,首先先启动测试程序,模拟cpu升高的情况
![e230f5d0ce2f88db1d58adca771ac4b9.png](https://i-blog.csdnimg.cn/blog_migrate/6fe236e561df7a832f366dbfd6a90a57.png)
然后启动arthas,选择测试程序的那个进程进行监控。使用dashboard可以看到id=10的这个线程占用cpu过高
![cb703df39a94e3b4b6919c9ac9fc0216.png](https://i-blog.csdnimg.cn/blog_migrate/84fdd25b825bc59753409d2fa4e1148e.jpeg)
打印id=10的线程堆栈信息,可以看到这个线程正在执行high()方法
![7d2b6adf1ebf45c9334c86a541eb87b9.png](https://i-blog.csdnimg.cn/blog_migrate/55d54d07f409872daec320be1d2e4272.png)
反编译hign方法,查看源码,可以看到进行了死循环。去除死循环后,cpu占比降低
![90f4160b049ac5be33b1d02e09cffe56.png](https://i-blog.csdnimg.cn/blog_migrate/07c33712bafe9aead7677cb9dca01438.png)
方法响应速度慢的问题调试
一个接口调用,好长时间才能收到响应,接口逻辑复杂,排查起来特别困难,不知从何下手?下面带你一步步找到问题
找到调用耗时较长的接口,这里测试使用的是MethodDemo类中的slow()方法,在arthas监控页面输入如下命令进行耗时统计
trace *MethodDemo slow
启动测试程序,调用slow方法,可以看到该方法耗时10秒
![41445251e2ee593142d927e39ac163f1.png](https://i-blog.csdnimg.cn/blog_migrate/d603088396088d64819fdfefafcd97f0.png)
实际开发中,接口很少加时间统计,arthas的trace可以清晰的显示方法耗时
![ecfd55136981d3604916a7446fb9ba4c.png](https://i-blog.csdnimg.cn/blog_migrate/06a8baf7f03ce0e62b7a5b25ffc734e5.png)
反编译查看代码,进行了sleep操作,所以耗时较长
![9805a07f0ca17c55d14b3020d24ac86a.png](https://i-blog.csdnimg.cn/blog_migrate/63685696f7f1b0c0401d7b4c7ccd929e.png)
trace命令是统计方法的内部调用路径,并为每个调用路径统计耗时。后面需要加上类名和参数名,支持模糊匹配
线程阻塞的调试
线程阻塞会导致并发量上不去,cpu占用过高,危害也非常大。
启动测试程序,调用演示线程阻塞的方法
![a7f5e81f6b932899931a7c6f59987088.png](https://i-blog.csdnimg.cn/blog_migrate/09f05cbefb5487fa758e7773bbc12783.png)
进入arthas监控页面,找到目前正在阻塞其他线程的线程,可以看到loop方法中正在锁定一个字符串,阻塞了其他线程
![7adf2541f0b3443aa5bd31eea03c2ff5.png](https://i-blog.csdnimg.cn/blog_migrate/bf0a0b59e812b7518015ba71f486e241.jpeg)
反编译loop方法,可以看到使用了synchronized进行了同步操作,阻塞了其他线程
![f3a1ef6424c9a87caa8cdb61334e11f8.png](https://i-blog.csdnimg.cn/blog_migrate/1e056a8c3acd61703b785225eb3cb91f.png)
由于篇幅有限,本文只讲一些arthas常用操作,后期会再出一篇高级操作,包括webconsole远程监控服务器、ognl表达式、封装docker镜像等
本文中使用的测试程序源码已整理放在公众号后台,关注赫连小伍公众号,回复arthas即可获取
公众号 赫连小伍 ,号内回复arthas可会获取本文中使用的demo源码和arthas官方离线文档