Arthas之thread、jad、sc、sm、ognl命令

Arthas是Alibaba开源的一款Java诊断工具,其提供了丰富的命令。这里介绍其中的thread、jad、sc、sm、ognl命令

abstract.png

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

figure 1.jpeg

然后通过指定线程ID查看线程堆栈信息,即可辅助我们定位到代码

figure 2.jpeg

下述通过创建线程模拟多个线程繁忙的现象

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();

// 保证main线程不停
while (true) { }
}
}

查看CPU使用率的Top K个线程,效果如下所示

figure 3.jpeg

figure 4.jpeg

下述代码模拟了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();
// 延时, 保证B线程启动时, A线程已经持有锁了
Thread.sleep(20*1000);
threadB.start();

// 保证main线程不停
while (true) { }
}
}

通过thread命令不难看出,A线程运行、B线程被阻塞。进一步通过指定线程ID来查看B线程的堆栈信息,可以看出其欲获取的锁信息、持锁线程信息;而通过-b选项我们则可以直接看出持锁线程A阻塞了其他线程

figure 5.jpeg

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

figure 6.jpeg

查看TestJad类的反编译结果,不显示源码行号。输出结果不包含ClassLoader信息。

1
jad --lineNumber false --source-only com.aaron.Arthas.TestJad

figure 7.jpeg

此外还可以在类名后面添加方法名来查看该类中指定方法的反编译结果

figure 8.jpeg

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

figure 9.jpeg

查看类的详细信息,包含字段信息

1
sc -d -f com.aaron.Arthas.Person

figure 10.jpeg

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

figure 11.jpeg

查看类中方法的详细信息

1
2
# 查看类下的方法信息
sm -d com.aaron.Arthas.Person

figure 12.jpeg

查看类中指定方法的详细信息

1
2
# 查看类中getPersons方法的详细信息
sm -d com.aaron.Arthas.Person getPersons

figure 13.jpeg

ognl

Arthas中提供了对orgnl表达式的支持,测试代码如下所示

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
# 查看该类下的静态变量
@class@field

figure 14.jpeg

调用类的静态方法

1
2
3
4
5
# 调用类的无参静态方法
@class@method()

# 调用类的有参静态方法
@class@method(args)

figure 15.jpeg

查看集合中元素

对于List、Map而言, 可分别通过指定索引、键名的方式来查看集合中的元素

1
2
3
4
5
# 查看List中下标索引为2的元素
list[2]

# 查看Map中键名为firstname的元素
map['firstname']

figure 16.jpeg

调用对象的方法

对于对象而言,类似于Java语法一样可通过点语法进行方法调用

1
2
3
4
5
# 调用类的无参方法
obj.method()

# 调用类的有参方法
obj.method(args)

figure 17.jpeg

0%