Arthas

 github https://github.com/alibaba/arthas

Arthas 是Alibaba开源的Java诊断工具,采用命令行交互模式,是排查jvm相关问题的利器。

java文件hash值比较 java has-a_java文件hash值比较

下载安装

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

java -jar arthas-boot.jar

# 然后可以选择一个Java进程

常用命令

具体每个命令怎么使用,大家可以自己查阅资料

version:查看arthas版本号 
help:查看命名帮助信息 
cls:清空屏幕 
session:查看当前会话信息 
quit:退出arthas客户端 
--- 
dashboard:当前进程的实时数据面板 
thread:当前JVM的线程堆栈信息 
jvm:查看当前JVM的信息 
sysprop:查看JVM的系统属性 
--- 
sc:查看JVM已经加载的类信息 
dump:dump已经加载类的byte code到特定目录 
jad:反编译指定已加载类的源码 
--- 
monitor:方法执行监控 
watch:方法执行数据观测 
trace:方法内部调用路径,并输出方法路径上的每个节点上耗时 
stack:输出当前方法被调用的调用路径 
......

下面我们看一些Arthas常用命令

问题1. 如何查找某个只知道大概的类,或者说想确认某个类是否已被系统加载,又或者说想确认加载的某个类属于哪个包?

sc *StringUtil      # 即可以找到需要的类全路径,如果存在的话
sc -d org.springframework.web.servlet.DispatcherServlet # 查看某个类的信息,如果存在的话 
sm org.springframework.web.servlet.DispatcherServlet getHandler # 查看某个方法的信息,如果存在的话

 

java文件hash值比较 java has-a_Boo_02

使用通配符列出所有的方法:命令:sm

java文件hash值比较 java has-a_Boo_03

问题2. 如何查看一个class的具体信息,或者说如何反编译class文件? 

jad java.lang.String # 反编译出java源代码

 

java文件hash值比较 java has-a_运维_04

问题3. 如何跟踪某个方法的返回值、入参… ?

 watch 让你能方便地观察到指定方法的调用情况。能观察到的范围为:入参、返回值、抛出异常

USAGE:
  watch [-b] [-e] [-x <value>] [-f] [-h] [-n <value>] [--listenerId <value>] [-E] [-M <value>] [-s] [-v] class-patter n method-pattern [express] [condition-express]

Examples:
  watch -b org.apache.commons.lang.StringUtils isBlank params
  watch -f org.apache.commons.lang.StringUtils isBlank returnObj
  watch org.apache.commons.lang.StringUtils isBlank '{params, target, returnObj}' -x 2
  watch -bf *StringUtils isBlank params
  watch *StringUtils isBlank params[0]
  watch *StringUtils isBlank params[0] params[0].length==1
  watch *StringUtils isBlank params '#cost>100'
  watch -E -b org\.apache\.commons\.lang\.StringUtils isBlank params[0]

java文件hash值比较 java has-a_运维_05

 1. 观察方法返回结果 returnObj

使用方式看着复杂,其实很简单。来个最简单的示例: 假设我们要观察下面这段代码中字符串的contains方法。 

public class App {
    public static void main(String[] args) throws IOException {
        String hello = "Hello Arthas";
        while (true) {
            boolean contains = StringUtils.contains(hello, "Arthas");
            System.out.println(contains);
        }
    }
}

 可以使用如下语句:

## 观察 contains 返回结果
[arthas@76792]$ watch org.apache.commons.lang3.StringUtils contains returnObj -n 3
# Press Q or Ctrl+C to abort.
# Affect(class-cnt:1 , method-cnt:2) cost in 68 ms.
# ts=2020-05-02 16:46:04; [cost=2.424254ms] result=@Boolean[true]
# ts=2020-05-02 16:46:05; [cost=0.21033ms] result=@Boolean[true]
# ts=2020-05-02 16:46:06; [cost=0.165514ms] result=@Boolean[true]

-n 3 表示只执行三次,这参数挺常用,不然很容易被输出刷屏。

2. 过滤不关心的调用 condition-express

 显然,真实的案例肯定不会如上面的示例那么简单。 真实的服务代码中,肯定不止一个地方调用了String的contains方法。我们需要把无关的调用过滤掉。

## 观察 contains 返回结果,并且过滤掉无关调用
[arthas@76792]$ watch org.apache.commons.lang3.StringUtils contains returnObj 'params[1]=="Arthas"'
# Press Q or Ctrl+C to abort.
# Affect(class-cnt:1 , method-cnt:2) cost in 29 ms.
# ts=2020-05-02 16:48:50; [cost=0.331109ms] result=@Boolean[true]
# ts=2020-05-02 16:48:51; [cost=0.175224ms] result=@Boolean[true]
# ts=2020-05-02 16:48:52; [cost=0.138984ms] result=@Boolean[true]

 入参是一个很容易把不同调用区分开的方法,通过params[1]=="Arthas"这个condition-express,我们可以只保留第二个入参是Arthas的函数调用。

3. 同时观察入参和结果 

[arthas@76792]$ watch org.apache.commons.lang3.StringUtils contains {params,returnObj} 'params[1]=="Arthas"'
# Press Q or Ctrl+C to abort.
# Affect(class-cnt:1 , method-cnt:2) cost in 33 ms.
# ts=2020-05-02 16:51:27; [cost=0.507486ms] result=@ArrayList[
#     @Object[][isEmpty=false;size=2],
#     @Boolean[true],
# ]

 通过 {} 把字段包起来,可以同时观察想观察的字段。可以注意到一个点,params 是一个数组,但是打印 params 的时候并没有把具体内容打印出来,这个时候可以使用 -x 2 来指定打印对象的属性遍历深度。

arthas@76792]$ watch org.apache.commons.lang3.StringUtils contains  {params,returnObj} 'params[1]=="Arthas"' -x 2
# Press Q or Ctrl+C to abort.
# Affect(class-cnt:1 , method-cnt:2) cost in 35 ms.
# ts=2020-05-02 16:51:33; [cost=0.391218ms] result=@ArrayList[
#     @Object[][
#         @String[Hello Arthas],
#         @String[Arthas],
#     ],
#     @Boolean[true],
# ]

 再举例子说明 -x 2 来指定打印对象的属性遍历深度

java文件hash值比较 java has-a_运维_06

java文件hash值比较 java has-a_java文件hash值比较_07

问题4. 查看最繁忙的线程,以及是否有阻塞情况发生? 

thread -n 3 # 查看最繁忙的三个线程栈信息
thread  # 以直观的方式展现所有的线程情况
thread -b #找出当前阻塞其他线程的线程

 

java文件hash值比较 java has-a_apache_08

问题5. 如何验证自己的代码猜想,临时更改代码运行? 

jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java  # 先反编译出class源码
 # 然后使用外部工具编辑内容
mc /tmp/UserController.java -d /tmp  # 再编译成class
 # 最后,重新载入定义的类,就可以实时验证你的猜测了
redefine /tmp/com/example/demo/arthas/user/UserController.class

 如上,是直接更改线上代码的方式,但是一般好像是编译不成功的。所以,最好是 本地ide 编译成 class 文件后,再上传替换为好!

问题6. 我如何测试某个方法的性能问题? 

monitor com.telek.business.statistics.util.tools.DzjcUtil sortByNumber -c 5

 

java文件hash值比较 java has-a_Boo_09

 

 

 问题7. 如果内存满了,想分析内存怎么办?

heapdump [-h] [-l] [file]

 

java文件hash值比较 java has-a_Boo_10

 到这那么问题来了,堆快照导出来了,之后怎么分析?

上工具:MemoryAnalyzer(MAT)

1、下载MAT

MAT是使用非常广泛的Java内存分析工具,功能强大; 

(1)官网下载:https://www.eclipse.org/mat/downloads.php 

java文件hash值比较 java has-a_运维_11

 

(2)解压后配置,如果快照文件太大可以通过配置该文件,默认:-Xmx1024m

 

java文件hash值比较 java has-a_java文件hash值比较_12

(3)运行 

 

java文件hash值比较 java has-a_apache_13

java文件hash值比较 java has-a_apache_14

 

 2、MAT打开分析HPROF文件

java文件hash值比较 java has-a_Boo_15

  

java文件hash值比较 java has-a_运维_16