Arthas是Alibaba开源的一款Java诊断工具,其提供了丰富的命令。这里介绍其中的thread、jad、sc、sm、ognl命令
thread命令
命令参数
该命令可以查看线程相关信息。该命令常用地选项有:
- —all :显示所有匹配的线程
- -i <num> :设置cpu统计时的采样间隔,单位为毫秒
- <id> :查看指定ID的线程堆栈
- -n <num> :查看CPU使用率最高的TopK个线程。如果值为-1表示显示所有线程
- -b :发现阻塞其他线程的持锁线程
实践
下述代码模拟CPU飙升
1 2 3 4 5 6 7 8 9
| public class TestThread { @Test public void test1() { while (true) { double num = Math.random(); System.out.println("Msg " + num); } } }
|
可以看出ID为1、线程名为main的线程大量消耗CPU
然后通过指定线程ID查看线程堆栈信息,即可辅助我们定位到代码
下述通过创建线程模拟多个线程繁忙的现象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class TestThread { @Test public void test2() { Runnable task = () -> { while (true) { double num1 = Math.random(); double num2 = Math.random(); double num3 = num1 + num2; } };
Thread threadA = new Thread(task, "线程A"); Thread threadB = new Thread(task, "线程B");
threadA.start(); threadB.start();
while (true) { } } }
|
查看CPU使用率的Top K个线程,效果如下所示
下述代码模拟了A线程持有锁,同时阻塞了B线程的场景
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
| public class TestThread { @Test public void test3() throws Exception { Object obj = new Object(); Runnable taskA = () -> { synchronized (obj) { while (true) { System.out.println("This is A thread"); } } };
Runnable taskB = () -> { synchronized (obj) { while (true) { System.out.println("This is B thread"); } } };
Thread threadA = new Thread(taskA, "A Test Thread"); Thread threadB = new Thread(taskB, "B Test Thread");
threadA.start(); Thread.sleep(20*1000); threadB.start();
while (true) { } } }
|
通过thread命令不难看出,A线程运行、B线程被阻塞。进一步通过指定线程ID来查看B线程的堆栈信息,可以看出其欲获取的锁信息、持锁线程信息;而通过-b选项我们则可以直接看出持锁线程A阻塞了其他线程
jad命令
其可以对指定已加载类进行反编译,该命令常用地选项有:
- —lineNumber [true/false] :是否显示代码行号, 默认为true
- —source-only :默认反编译结果会包含ClassLoader信息、源码。使用该选项后,将会只展示源码
这里提供如下的测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| package com.aaron.Arthas;
import org.junit.Test;
public class TestJad { @Test public void test1() throws InterruptedException { while (true) { generateInfo(); Thread.sleep( 1000); } }
private void generateInfo() { Double money = calcMoney(); String info = "I get Money: " + money + "$"; System.out.println(info); }
public Double calcMoney() { return Math.random(); } }
|
查看TestJad类的反编译结果
1
| jad com.aaron.Arthas.TestJad
|
查看TestJad类的反编译结果,不显示源码行号。输出结果不包含ClassLoader信息。
1
| jad --lineNumber false --source-only com.aaron.Arthas.TestJad
|
此外还可以在类名后面添加方法名来查看该类中指定方法的反编译结果
sc
该命令查看JVM已加载的类信息,测试代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package com.aaron.Arthas;
import java.util.ArrayList;
public class Person extends ArrayList { private static String code = "0x7E";
public static boolean flag = true;
private String name;
private int age;
public static void main(String[] args) throws InterruptedException { while (true) { Thread.sleep(2*1000); } } }
|
查看类的详细信息
1
| sc -d com.aaron.Arthas.Person
|
查看类的详细信息,包含字段信息
1
| sc -d -f com.aaron.Arthas.Person
|
sm
该命令查看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
| package com.aaron.Arthas;
import java.util.ArrayList; import java.util.Arrays; import java.util.List;
public class Person extends ArrayList { private String name;
private int age;
public static void main(String[] args) throws InterruptedException { while (true) { Thread.sleep(2*1000); } }
@Deprecated private Integer getAge(Double facotr) { int res = (int)(this.age * facotr); return res; }
private Person() { }
public Person(String name) { this.name = name; }
public static List<Person> getPersons(Person[] persons) { return Arrays.asList(persons); } }
|
查看该类下所有方法,注意不包含父类的方法
1 2
| sm com.aaron.Arthas.Person
|
查看类中方法的详细信息
1 2
| sm -d com.aaron.Arthas.Person
|
查看类中指定方法的详细信息
1 2
| sm -d com.aaron.Arthas.Person getPersons
|
ognl
Arthas中提供了对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 50 51 52 53 54
| package com.aaron.Arthas;
import java.util.*;
public class Dog { private static List<String> types = Arrays.asList("泰迪", "柯基", "茶杯犬");
private static Map<String, Dog> cache = new HashMap<>();
public static double price = 199.35;
private String name;
private int type;
public Dog(String name, int type) { this.name = name; this.type = type; }
public String getInfo() { String info = "<Dog>: name -> "+name+", type -> " +types.get(type); return info; }
public static void main(String[] args) throws InterruptedException { Dog dog1 = new Dog("Tom", 2); System.out.println(dog1.getInfo()); Dog dog2 = new Dog("Bob", 0); System.out.println(dog2.getInfo()); cache.put("dog1", dog1); cache.put("dog2", dog2); while (true) { Thread.sleep(2*1000); } }
public static double calcAllPrice(Integer num) { double allPrice = num * price; return allPrice; } public static String getType() { return types.stream() .collect(Collectors.joining(",")); }
public static String getType(int index) { return types.get(index); } }
|
查看静态变量
调用类的静态方法
1 2 3 4 5
| @class@method()
@class@method(args)
|
查看集合中元素
对于List、Map而言, 可分别通过指定索引、键名的方式来查看集合中的元素
1 2 3 4 5
| list[2]
map['firstname']
|
调用对象的方法
对于对象而言,类似于Java语法一样可通过点语法进行方法调用
1 2 3 4 5
| obj.method()
obj.method(args)
|