根据经验我们知道,cpu爆满的原因无非两种,1、形成了死循环,2、形成了死锁。
前文:
top 命令是 Linux 下常用的系统资源占用查看及性能分析工具,能够实时显示系统中各个进程的资源(比如cpu、内存的使用)占用状况。它后面可以跟很多的参数,来进行具体分析。常见的参数有如下一些,不完全。
-c: 命令行列显示程序名以及参数 -d: 启动时设置刷新时间间隔 -H: 设置线程模式 -i: 只显示活跃进程 -n: 显示指定数量的进程 -p: 显示指定PID的进程 -u: 显示指定用户的进程 这里,根据需要,需要使用 -H: 设置线程模式 以及 -p: 显示指定PID的进程 两个参数。
总结:
- Linux top 命令查询当前资源的使用情况 查找出使用率比较高的进程 pid
- 根据 top -Hp pid 查找出 当前进程pid下 资源占用比较高的线程id
- 将当前线程id 转换为16进制的数值 。printf "%x\n" 线程id
- 查看当前的堆栈信息。jstack pid |grep 16进制线程id -C 30 打印上下15行记录
- 根据当前堆栈信息 可以查看当前耗资源线程的运行逻辑。方便继续排查
- ps -ef|grep pid 查看当前进程 运行的程序情况
- 举个例子比如发现当前线程的内容是某个后台agent指标采集的线程jmx。后续可以根据netstat -anp|grep 指标采集任务的port端口 查看监听状态。
- netstat -anp | grep {jmx采集端口号} 查看jmx采集端口的连接情况,看到有非常多的LISTING 状态为 CLOSE_WAIT连接
举例1.如何用如何用jstack排查死锁问题
先来看一段会产生死锁的Java程序,源码如下:
/**
* Java 死锁demo
*/
public class DeathLockTest {
private static Lock lock1 = new ReentrantLock();
private static Lock lock2 = new ReentrantLock();
public static void deathLock() {
Thread t1 = new Thread() {
@Override
public void run() {
try {
lock1.lock();
System.out.println(Thread.currentThread().getName() + " get the lock1");
Thread.sleep(1000);
lock2.lock();
System.out.println(Thread.currentThread().getName() + " get the lock2");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Thread t2 = new Thread() {
@Override
public void run() {
try {
lock2.lock();
System.out.println(Thread.currentThread().getName() + " get the lock2");
Thread.sleep(1000);
lock1.lock();
System.out.println(Thread.currentThread().getName() + " get the lock1");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
//设置线程名字,方便分析堆栈信息
t1.setName("mythread-jay");
t2.setName("mythread-tianluo");
t1.start();
t2.start();
}
public static void main(String[] args) {
deathLock();
}
}
运行结果:
显然,线程jay和线程tianluo都是只执行到一半,就陷入了阻塞等待状态~
jstack排查Java死锁步骤
- 在终端中输入jps查看当前运行的java程序 或者ps -ef|grep java
- 使用 jstack -l pid 查看线程堆栈信息
- 分析堆栈信息
举例2 jstack 分析CPU过高问题
来个导致CPU过高的demo程序,一个死循环,哈哈~
/**
* 有个导致CPU过高程序的demo,死循环
*/
public class JstackCase {
private static ExecutorService executorService = Executors.newFixedThreadPool(5);
public static void main(String[] args) {
Task task1 = new Task();
Task task2 = new Task();
executorService.execute(task1);
executorService.execute(task2);
}
public static Object lock = new Object();
static class Task implements Runnable{
public void run() {
synchronized (lock){
long sum = 0L;
while (true){
sum += 1;
}
}
}
}
}
jstack 分析CPU过高步骤
- top
- top -Hp pid 查找耗资源的线程id
- jstack pid |grep 线程id
- jstack -l [PID] >/tmp/log.txt
- 分析堆栈信息
1.top
在服务器上,我们可以通过top命令查看各个进程的cpu使用情况,它默认是按cpu使用率由高到低排序的
由上图中,我们可以找出pid为10761的java进程,它占用了最高的cpu资源,凶手就是它,哈哈!
2. top -p pid -H
通过top -p 10761 -H 可以查看该进程下,各个线程的cpu使用情况,如下:
可以发现pid为10771的线程,CPU资源占用最高~,嘻嘻,小本本把它记下来,接下来拿jstack给它拍片子~
3. jstack pid
先将线程id为10771 转换为16进制数值
通过top命令定位到cpu占用率较高的线程之后,接着使用jstack pid命令来查看当前java进程的堆栈状态,jstack
10761 |grep "2a13" -A 30后,查看对应的堆栈信息。
4. jstack -l [PID] >/tmp/log.txt
其实,前3个步骤,堆栈信息已经出来啦。但是一般在生成环境,我们可以把这些堆栈信息打到一个文件里,再回头仔细分析哦~
5. 分析堆栈信息
我们把占用cpu资源较高的线程pid(本例子是10771),将该pid转成16进制的值 为2a13
在thread dump中,每个线程都有一个nid,我们找到对应的nid(2a13)观察具体的逻辑内容。