jstack 命令的使用和问题排查分析思路
jstack 命令什么是jstackjstack命令jstack实战操作什么是jstackjstack是用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内存每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因如果出现死锁,死循环,请求外部资源出现长时间等待等,线程出现停顿的时候,通过jstack来查看各个线程调用堆栈,就知道线程在后台做了什
jstack 命令
-
什么是jstack
-
jstack命令
-
jstack实战操作
什么是jstack
jstack是用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内存每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因
如果出现死锁,死循环,请求外部资源出现长时间等待等,线程出现停顿的时候,通过jstack来查看各个线程调用堆栈,就知道线程在后台做了什么,或者等待什么资源,如果java程序崩溃生成core文件,jstack工具可以用来获取core文件的java,stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程
程序触发生成的问题,另外,jstack工具还可以附属到正在运行的java程序中看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现bug的状态,jstack是非常有用的。
jstack命令
参数说明:
-
-F 当 ‘jstack [-l] pid’ 没有响应的时候强制打印栈信息,如果直接jstack无响应时,用于强制jstack,一般情况下无需使用
-
-l 长列表 打印关于锁定附加信息,例如属于:java.util.concurrent的ownable synchronizers列表会使jvm停顿的长久的多,
-
-m 打印java和native c/c++ 框架的所有栈信息.可以打印JVM的堆栈,显示上Native的栈帧,一般应用排查不需要使用
执行命令
jstack -m 12905
科普一下线程状态:
NEW | 未启动的。不会出现在Dump中 |
---|---|
RUNNABLE | 在虚拟机内执行的 |
BLOCKED | 受阻塞并等待监视器锁 |
WATING | 无限期等待另一个线程执行特定操作。 |
TIMED_WATING | 有时限的等待另一个线程的特定操作 |
TERMINATED | 已退出的 |
jstack实战操作
现在假设一个场景,在几乎没有什么调用的情况下,服务器的cpu的使用率一直居高不下非常有可能是程序中出现死循环导致的,当然在业务量不大的情况下,如果cpu占用率过高,除了死循环以外,常见的还有,内存泄漏,导致大量的full GC
jstack实战之cpu 占用过高-----测试代码
/**
* @Auther: corey
* @Date: 2020/8/5 14:25
* @Description: jstack 测试代码
*/
public class JstackTest {
private static Executor executor = Executors.newFixedThreadPool(5);
private static final Object lock = new Object();
public static void main(String[] args) {
MyRunnableImpl myRunnable = new MyRunnableImpl();
MyRunnableImpl myRunnable1 = new MyRunnableImpl();
executor.execute(myRunnable);
executor.execute(myRunnable1);
}
static class MyRunnableImpl implements Runnable{
@Override
public void run() {
synchronized (lock){
//死循环
calculate();
}
}
private void calculate(){
int i = 0;
while (true){
i++;
}
}
}
}
使用top命令可以看到,cpu的占用到99%了,使用 jps命令查看当前java 程序运行的有哪些。
然后使用 jstack 4275 命令查看当前程序出现死循环的是在哪一行
如下上图,JstackTest 程序的 31行
①线程名。
②线程优先级。
③一个地址(该地址是什么地址,存疑)。
④线程十六进制id。
⑤线程状态。
⑥线程当前所处方法。
⑦该箭头表示线程的执行历程。
jstack实战之死锁线程的定位-----测试代码
/**
* @Auther: corey
* @Date: 2020/8/5 15:01
* @Description:死锁线程的定位
*/
public class JstackLockTest {
private static Executor executor = Executors.newFixedThreadPool(5);
private static final Object lockA = new Object();
private static final Object lockB = new Object();
public static void main(String[] args) {
MyRunnableImplOne th1 = new MyRunnableImplOne();
MyRunnableImplTwo th2 = new MyRunnableImplTwo();
executor.execute(th1);
executor.execute(th2);
}
static class MyRunnableImplOne implements Runnable {
@Override
public void run() {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + "获得了锁LockA");
//循环
int i = 10;
while (i > 10) {
i--;
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + "获得了锁B");
}
}
}
}
static class MyRunnableImplTwo implements Runnable {
@Override
public void run() {
synchronized (lockB) {
System.out.println(Thread.currentThread().getName()+"获得了锁lockB");
int i = 10;
while (i > 10) {
i--;
}
synchronized (lockA) {
System.out.println(Thread.currentThread().getName()+"获得了锁lockA");
}
}
}
}
}
输出如下:
如上图所示没有继续输出;继续排查
使用 jps 命令查看
使用jstack pid指令,查看指定进程的堆栈信息,观察并定位到死锁线程。
上图可以看到,线程 pool-1-thread-2已经获取到了地址 <0x000000076ae309a0> 正在 java.lang.Thread.State: BLOCKED 等待获取地址为 <0x000000076ae309b0> 的对象锁,
第二个thread所示:
pool-1-thread-1 已经获取到了地址为<0x000000076ae309b0>的地址,正在BLOCKED等待地址为 <0x000000076ae309a0>的对象锁
上述分析过程是对死锁和cpu 超高的情况下的场景分析,我们可以通过诸如【统一获取锁的顺序(线程按照一定的顺序加锁)】、【设置获取锁的超时时间(线程尝试获, 取锁的时候加上一定的时限,超过时限则放弃对该锁的请求,并释放自己占有的锁)】、【死锁检测】等手段预防死锁发生。但是如果一旦发生了死锁就没法解开了,只能停掉程序,修复bug后再启动服务了。
参考:https://www.cnblogs.com/taiguyiba/p/9470861.html
https://blog.csdn.net/justry_deng/article/details/90447410
更多推荐
所有评论(0)