写点什么

JVM 调优神器 arthas

作者:java易二三
  • 2023-07-30
    湖南
  • 本文字数:4839 字

    阅读完需:约 16 分钟

一、安装

arthas 在 github 上有个 page,地址是https://alibaba.github.io/arthas/

安装的方式有好几种:

  1. 直接下载一个可以启动的 jar 包然后用 java -jar 的方式启动

  2. 用官方提供的 as.sh 脚本一键安装

  3. 用 rpm 的方式安装

本篇介绍第一种方式,因为它简单而且想迁移的时候也超级方便(毕竟只需要把下载的 jar 包拷贝走就行了)。

curl -O https://alibaba.github.io/arthas/arthas-boot.jar

复制代码

如果下载速度太慢,可以用 gitee 上的源

curl -O https://arthas.gitee.io/arthas-boot.jar

复制代码

curl 命令直接把 arthas-boot.jar 下载到你想要的目录

[root@localhost ~]# ll -lrt-rw-r--r--. 1 root root   138880 Jun 22 02:55 arthas-boot.jar

复制代码

二、启动

用 java 命令直接启动

[root@localhost ~]# java -jar arthas-boot.jar [INFO] arthas-boot version: 3.3.3[INFO] Can not find java process. Try to pass <pid> in command line.Please select an available pid.

复制代码

但是这里启动失败了,这是因为 arthas 在启动时会检测本机运行的 jvm 进程,然后让用户选择需要绑定的进程,后面的操作都是针对选定的进程的。

这里我先启动一个 java 应用,然后再启动 arthas。

[root@localhost ~]# java -jar arthas-boot.jar [INFO] arthas-boot version: 3.3.3[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1\. Then hit ENTER.* [1]: 2467 jvm-0.0.1-SNAPSHOT.jar

复制代码

下面就列出了本机正在运行的 java 进程,等待用户输入,这里输入 1 然后回车。如果是第一次启动需要下载一些必要的文件,等待下载完成即可。

[root@localhost arthas]# java -jar arthas-boot.jar [INFO] arthas-boot version: 3.3.3[INFO] Found existing java process, please choose one and input the serial number of the process, eg : 1\. Then hit ENTER.* [1]: 2467 jvm-0.0.1-SNAPSHOT.jar1[INFO] arthas home: /usr/local/arthas[INFO] Try to attach process 2467[INFO] Attach process 2467 success.[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.3 pid 2467 time 2020-06-22 03:02:31
[arthas@2467]$

复制代码

如果看到这个界面就表示启动并关联成功了。

三、help 命令

在 arthas 交互环境中,可以输入 help 命令,然后会出现所有 arthas 支持的命令

[arthas@2467]$ help NAME         DESCRIPTION                       help         Display Arthas Help                                        keymap       Display all the available keymap for the specified connection.   sc           Search all the classes loaded by JVM        sm           Search the method of classes loaded by JVM       classloader  Show classloader info  jad          Decompile class  getstatic    Show the static field of a class      ...

复制代码

如果不知道命令的用法,可以输入相应的命令后加参数--help,比如可以看一下 sc 命令的用法

[arthas@2467]$ sc --help USAGE:                                       sc [-c <value>] [-d] [-x <value>] [-f] [-h] [-E] class-pattern                                                                                      SUMMARY:                                                                                                                                Search all the classes loaded by JVM                                                                                                        EXAMPLES:                                      sc -d org.apache.commons.lang.StringUtils      sc -d org/apache/commons/lang/StringUtils      sc -d *StringUtils                             sc -d -f org.apache.commons.lang.StringUtils                                           sc -E org\\.apache\\.commons\\.lang\\.StringUtils                                                                                  
WIKI: https://alibaba.github.io/arthas/sc OPTIONS: -c, --classloader <value> The hash code of the special class's classLoader -d, --details Display the details of class -x, --expand <value> Expand level of object (0 by default) -f, --field Display all the member variables -h, --help this help -E, --regex Enable regular expression to match (wildcard matching by default) <class-pattern> Class name pattern, use either '.' or '/' as separator

复制代码

不仅会显示出命令是干嘛用的,命令的完整参数,还很贴心地提供了一些具体的例子,如果英语看不习惯,还可以到 WIKI 下面那个地址看官方文档,有中文版的。

四、用 arthas 解决上一篇的问题

(1)cpu 占用过高

用 thread 命令列出线程的信息

[arthas@2467]$ threadThreads Total: 28, NEW: 0, RUNNABLE: 11, BLOCKED: 0, WAITING: 14, TIMED_WAITING: 3, TERMINATED: 0                                     ID         NAME                              GROUP                 PRIORITY   STATE      %CPU        TIME       INTERRUPTE DAEMON     16         http-nio-8080-exec-2              main                  5          RUNNABLE   99          0:25       false      true       29         Attach Listener                   system                9          RUNNABLE   0           0:0        false      true       11         Catalina-utility-1                main                  1          WAITING    0           0:0        false      false      12         Catalina-utility-2                main                  1          TIMED_WAIT 0           0:0        false      false      28         DestroyJavaVM                     main                  5          RUNNABLE   0           0:4        false      false      3          Finalizer                         system                8          WAITING    0           0:0        false      true       2          Reference Handler                 system                10         WAITING    0           0:0        false      true    

复制代码

这个命令会把所有线程按照 cpu 占用率从高到低列出来,如果线程太多,可以通过-n 参数指定输出的行数。

上面的输出结果可以看到 id 为 16 的这个线程 cpu 占用率很过,然后再通过 thread 加线程 id 输出改线程的栈信息

[arthas@2467]$ thread 16"http-nio-8080-exec-2" Id=16 RUNNABLE    at com.spareyaya.jvm.service.EndlessLoopService.service(EndlessLoopService.java:19)    at com.spareyaya.jvm.controller.JVMController.endlessLoop(JVMController.java:30)    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)    ...

复制代码

两步就定位到了问题

(2)死锁

还是用 thread 命令,参数是-b

[arthas@2997]$ thread -b"Thread-3" Id=29 BLOCKED on java.lang.Object@3f20bf9 owned by "Thread-4" Id=30    at com.spareyaya.jvm.service.DeadLockService.service1(DeadLockService.java:27)    -  blocked on java.lang.Object@3f20bf9    -  locked java.lang.Object@2fea801a <---- but blocks 1 other threads!    at com.spareyaya.jvm.controller.JVMController.lambda$deadLock$0(JVMController.java:37)    at com.spareyaya.jvm.controller.JVMController$$Lambda$456/748979989.run(Unknown Source)    at java.lang.Thread.run(Thread.java:748)

复制代码

这个命令和 jstack 工具检测死锁同样简单,不过个人认为 jstack 工具检测死锁其实要比这个更直观一些。

(3)内存泄漏

这个我们可以用 dashboard 命令来动态查看内存情况


如果内容使用率在不断上升,而且 gc 后也不下降,后面还发现 gc 越来越频繁,很可能就是内存泄漏了。

这个时候我们可以直接用 heapdump 命令把内存快照 dump 出来,作用和 jmap 工具一样

[arthas@23581]$ heapdump --live /root/jvm.hprofDumping heap to /root/jvm.hprof...Heap dump file created

复制代码

然后把得到的 dump 文件导入 eclipse,用 MAT 插件分析就行了。

五、arthas 其它命令

arthas 还提供了很多用于监控的命令,比如监控某个方法的执行时间,反编译线上的 class 文件,甚至在不重启 java 应用的情况下直接替换某个类。官方的使用文档已经写得太详细了,这里就不再一一介绍了,大家可以自己尝试。

六、再说 MAT 工具

上一篇和本篇在排查内存泄漏的时候我们都用到了同一个工具来分析——MAT。之前我们是在 eclipse 中安装了 MAT 插件,使用的时候只能打开 eclipse 来用。问题是,现在使用 eclipse 作为开发工具的移动互联网公司应该很少了,我们也不想每次分析内存快照时都要启动一个 eclipse。

所以这里介绍一个 MAT 的独立工具,它是独立于 eclipse 的应用,下载地址是https://www.eclipse.org/mat/downloads.php,可以根据自己的系统选择版本。

比如在 windows 下可以直接双击 MemoryAnalyzer.exe 启动,启动后可以通过顶部菜单的 File->Open Heap Dump...来打开一个快照文件,也可以在 welcome 界面中点击 Open a Heap Dump。如果你的快照文件特别大,需要调整 jvm 参数,在 windows 下修改 MemoryAnalyzer.ini 文件,把-Xmx 参数的值设置成适合的值(默认是 1024M)。


在 Overview 选择卡中,可以选择需要分析的内容。比如可以点击 Leak Suspects 分析可能的内存泄漏,也可以点击 Histogram 来查看每个类的实例统计。


然后重点关注那些实例数目特别多的,或者占用内存特别多的(这个还可以设置正则表达式进行过滤,在大项目时很有用),然后结合自己的代码看看这些对象是不是真正都需要的,还是因为作用域设置得太大了导致没有及时回收造成。

总之,分析内存快照其实是一项费时费力的工作,在分析中积累经验其它很重要,工具只是为了提高分析的效率。

至于像 JProfile 这种商业版专业的 jvm 分析工具,也可以去多了解。

用户头像

java易二三

关注

还未添加个人签名 2021-11-23 加入

还未添加个人简介

评论

发布
暂无评论
JVM 调优神器 arthas_编程_java易二三_InfoQ写作社区