0%

java应用在linux中性能和堆栈信息的排查

####简介
在线上环境中有时会遇到cpu等资源占用过多的资源我们可以使用top命令和jstack命令对问题进行排查

编写循环代码

下面代码开启两个线程循环打印

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
public class Test {

public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(2);
service.submit(new Runnable() {
@Override
public void run() {
runTest();
}
});


service.submit(new Runnable() {
@Override
public void run() {
runTest();
}
});


}


public static void runTest() {
while (true) {
System.out.println(1);
}
}
}

将代码发布到linux上并执行

开启jar应用

1
nohup java -jar test.jar >/dev/null 2>&1 &

使用top命令查看进程情况

我们可以从命令中看到pid 19384占用了大量资源

1
top

top进程命令

使用top命令查看线程情况

从上一步我们得到进程pid,现在我们使用top命令查看进程pid下的线程

1
top -Hp  19384

top线程命令
我们可以看到19436 和19437这两个线程占用了大量资源

使用jstack命令进行具体分析

根据进程pid进行查询

1
jstack -l 19437

jstack命令
我们由上面的top线程查询到19436和19437这两个线程占用了大量资源
将其转化成16进制分别为4bec和4bed
根据16进制的pid查询对应的nid,如图我们找到了pool-1-thread-2 这个线程名对应的nid为4bed,这个线程就是我们之前开启的一个线程,我们可以查看之中的堆栈信息发现其在runTest方法中持续执行,并且持续在执行io流(打印)

通过thread dump分析线程状态

除了上述的分析,大多数情况下会基于thead dump分析当前各个线程的运行情况,如是否存在死锁、是否存在一个线程长时间持有锁不放等等。在dump中,线程一般存在如下几种状态:

  • RUNNABLE,线程处于执行中
  • BLOCKED,线程被阻塞
  • WAITING,线程正在等待

问题

在实际调试中这两个线程无法并发执行,始终都是一个线程为RUNNABLE,一个为BLOCKED,但是实际代码并未加锁,根据堆栈打印出的信息线程中是在io流中加了锁

1
locked <0x0000000085c3d2e0> (a java.io.OutputStreamWriter)

于是查看System.out.println(“ “)的源代码,可以看出源码中打印代码时加了锁的,所以导致两个线程无法并发

1
2
3
4
5
6
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}

系统资源监控命令

  • vmstat
  • iostat