给你的Java程序拍个片子吧:jstack命令解析
前言
如果有一天,你的Java程序长时间停顿,也许是它病了,需要用jstack拍个片子分析分析,才能诊断具体什么病症,是死锁综合征,还是死循环等其他病症,本文我们一起来学习jstack命令~
- jstack 的功能
- jstack用法
- 线程状态等基础回顾
- 实战案例1:jstack 分析死锁
- 实战案例2:jstack 分析CPU 过高
jstack 的功能
jstack是JVM自带的Java堆栈跟踪工具,它用于打印出给定的java进程ID、core file、远程调试服务的Java堆栈信息.
jstack prints Java stack traces of Java threads for a given Java process or
core file or a remote debug server.
- jstack命令用于生成虚拟机当前时刻的线程快照。
- 线程快照是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,
如线程间死锁、死循环、请求外部资源导致的长时间等待等问题。- 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。
- 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。
- 另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。
jstack用法
jstack 命令格式如下
jstack [ option ] pid
jstack [ option ] executable core
jstack [ option ] [server-id@]remote-hostname-or-IP
- executable Java executable from which the core dump was produced.(可能是产生core dump的java可执行程序)
- core 将被打印信息的core dump文件
- remote-hostname-or-IP 远程debug服务的主机名或ip
- server-id 唯一id,假如一台主机上多个远程debug服务
最常用的是
jstack [option] <pid> // 打印某个进程的堆栈信息
option参数说明如下:
选项 | 作用 |
---|---|
-F | 当正常输出的请求不被响应时,强制输出线程堆栈 |
-m | 如果调用到本地方法的话,可以显示C/C++的堆栈 |
-l | 除堆栈外,显示关于锁的附加信息,在发生死锁时可以用jstack -l pid来观察锁持有情况 |
线程状态等基础回顾
线程状态简介
jstack用于生成线程快照的,我们分析线程的情况,需要复习一下线程状态吧,拿小凳子坐好,复习一下啦~
Java语言定义了6种线程池状态:
- New:创建后尚未启动的线程处于这种状态,不会出现在Dump中。
- RUNNABLE:包括Running和Ready。线程开启start()方法,会进入该状态,在虚拟机内执行的。
- Waiting:无限的等待另一个线程的特定操作。
- Timed Waiting:有时限的等待另一个线程的特定操作。
- 阻塞(Blocked):在程序等待进入同步区域的时候,线程将进入这种状态,在等待监视器锁。
- 结束(Terminated):已终止线程的线程状态,线程已经结束执行。
Dump文件的线程状态一般其实就以下3种:
- RUNNABLE,线程处于执行中
- BLOCKED,线程被阻塞
- WAITING,线程正在等待
Monitor 监视锁
因为Java程序一般都是多线程运行的,Java多线程跟监视锁环环相扣,所以我们分析线程状态时,也需要回顾一下Monitor监视锁知识。
有关于线程同步关键字Synchronized与监视锁的爱恨情仇,有兴趣的伙伴可以看一下我这篇文章
Synchronized解析——如果你愿意一层一层剥开我的心
Monitor的工作原理图如下:
- 线程想要获取monitor,首先会进入Entry Set队列,它是Waiting Thread,线程状态是Waiting for monitor entry。
- 当某个线程成功获取对象的monitor后,进入Owner区域,它就是Active Thread。
- 如果线程调用了wait()方法,则会进入Wait Set队列,它会释放monitor锁,它也是Waiting Thread,线程状态in Object.wait()
- 如果其他线程调用 notify() / notifyAll() ,会唤醒Wait Set中的某个线程,该线程再次尝试获取monitor锁,成功即进入Owner区域。
Dump 文件分析关注重点
- runnable,线程处于执行中
- deadlock,死锁(重点关注)
- blocked,线程被阻塞 (重点关注)
- Parked,停止
- locked,对象加锁
- waiting,线程正在等待
- waiting to lock 等待上锁
- Object.wait(),对象等待中
- waiting for monitor entry 等待获取监视器(重点关注)
- Waiting on condition,等待资源(重点关注),最常见的情况是线程在等待网络的读写
实战案例1:jstack 分析死锁问题
- 什么是死锁?
- 如何用jstack排查死锁?
什么是死锁?
死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法进行下去。
如何用如何用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死锁步骤
- 在终端中输入jsp查看当前运行的java程序
- 使用 jstack -l pid 查看线程堆栈信息
- 分析堆栈信息
在终端中输入jsp查看当前运行的java程序
通过使用 jps 命令获取需要监控的进程的pid,我们找到了23780 DeathLockTest
使用 jstack -l pid 查看线程堆栈信息
由上图,可以清晰看到死锁信息:
- mythread-tianluo 等待这个锁 “0x00000000d61ae3a0”,这个锁是由于mythread-jay线程持有。
- mythread-jay线程等待这个锁“0x00000000d61ae3d0”,这个锁是由mythread-tianluo 线程持有。
还原死锁真相
“mythread-tianluo"线程堆栈信息分析如下:
- mythread-tianluo的线程处于等待(waiting)状态,持有“0x00000000d61ae3d0”锁,等待“0x00000000d61ae3a0”的锁
“mythread-jay"线程堆栈信息分析如下:
- mythread-tianluo的线程处于等待(waiting)状态,持有“0x00000000d61ae3a0”锁,等待“0x00000000d61ae3d0”的锁
实战案例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
- jstack pid
- jstack -l [PID] >/tmp/log.txt
- 分析堆栈信息
1.top
在服务器上,我们可以通过top命令查看各个进程的cpu使用情况,它默认是按cpu使用率由高到低排序的
由上图中,我们可以找出pid为21340的java进程,它占用了最高的cpu资源,凶手就是它,哈哈!
2. top -Hp pid
通过top -Hp 21340可以查看该进程下,各个线程的cpu使用情况,如下:
可以发现pid为21350的线程,CPU资源占用最高,嘻嘻,小本本把它记下来,接下来拿jstack给它拍片子
3. jstack pid
通过top命令定位到cpu占用率较高的线程之后,接着使用jstack pid命令来查看当前java进程的堆栈状态,jstack 21350
后,内容如下:
4. jstack -l [PID] >/tmp/log.txt
其实,前3个步骤,堆栈信息已经出来啦。但是一般在生成环境,我们可以把这些堆栈信息打到一个文件里,再回头仔细分析哦~
5. 分析堆栈信息
我们把占用cpu资源较高的线程pid(本例子是21350),将该pid转成16进制的值
在thread dump中,每个线程都有一个nid,我们找到对应的nid(5366),发现一直在跑(24行)
这个时候,可以去检查代码是否有问题啦~ 当然,也建议隔段时间再执行一次stack命令,再一份获取thread dump,毕竟两次拍片结果(jstack)对比,更准确嘛~
参考与感谢
个人公众号
- 觉得写得好的小伙伴给个点赞+关注啦,谢谢~
- 如果有写得不正确的地方,麻烦指出,感激不尽。
- 同时非常期待小伙伴们能够关注我公众号,后面慢慢推出更好的干货~嘻嘻
- github地址:https://github.com/whx123/JavaHome
给你的Java程序拍个片子吧:jstack命令解析的更多相关文章
- 设计一个 Java 程序,自定义异常类,从命令行(键盘)输入一个字符串,如果该字符串值为“XYZ”。。。
设计一个 Java 程序,自定义异常类,从命令行(键盘)输入一个字符串,如果该字符串值为“XYZ”,则抛出一个异常信息“This is a XYZ”,如果从命令行输入 ABC,则没有抛出异常.(只有 ...
- Java 程序该怎么优化?(命令篇)
灵魂拷问,JDK 提供的命令,除了 java.javac,你还用过哪些命令呢? 灵魂再拷问,若你写的 Java 程序,出现了性能问题,该怎么去排查呢? Java 作为编程语言中的战斗机,JDK 默认已 ...
- Java程序员面试必备:Volatile全方位解析
前言 volatile是Java程序员必备的基础,也是面试官非常喜欢问的一个话题,本文跟大家一起开启vlatile学习之旅,如果有不正确的地方,也麻烦大家指出哈,一起相互学习~ 1.volatile的 ...
- Java 程序员必须掌握的 Linux 命令(转:导师Jencks)
1.查找文件 find / -name filename.txt根据名称查找/目录下的filename.txt文件. find . -name "*.xml"递归查找所有的xml文 ...
- Java 程序员必须掌握的 Linux 命令
作为一个Java开发人员,有些常用的Linux命令必须掌握.即时平时开发过程中不使用Linux(Unix)或者mac系统,也需要熟练掌握Linux命令.因为很多服务器上都是Linux系统.所以,要和服 ...
- Java程序员常用Linux性能分析命令
性能分析 vmstat 虚拟内存统计 用法 Usage: vmstat [options] [delay [count]] Options: -a, --active active/inactive ...
- Java程序员应该知道的linux命令
1.查看Java进程:ps -ef|grep java,ps auxf|grep jva; 2.杀死所有Java进程: pkill java, kill -9 进程ID: 3.进入目录:cd /usr ...
- 如何在java程序中调用linux命令或者shell脚本
转自:http://blog.sina.com.cn/s/blog_6433391301019bpn.html 在java程序中如何调用linux的命令?如何调用shell脚本呢? 这里不得不提到ja ...
- 20145223《Java程序程序设计》实验一实验报告
实验一 Java开发环境的熟悉(Windows + IDE) 实验内容 1.使用JDK编译.运行简单的Java程序: 2.使用IDE 编辑.编译.运行.调试Java程序. 实验步骤 (一)命令行下Ja ...
随机推荐
- D - Yet Another Monster Killing Problem
题目连接: https://codeforces.com/contest/1257/problem/D 题目大意: n个怪兽,m个英雄,每个怪兽有一定的能力值,每个英雄有一定的能力值和一定的耐力值.耐 ...
- 【论文研读】强化学习入门之DQN
最近在学习斯坦福2017年秋季学期的<强化学习>课程,感兴趣的同学可以follow一下,Sergey大神的,有英文字幕,语速有点快,适合有一些基础的入门生. 今天主要总结上午看的有关DQN ...
- Postman:Pre-request Script
Pre-request Script:前置处理,会在发出请求前执行,主要用在生成一些动态参数. 例如:api接口都会有签名校验,这个校验在我们api测试的时候很不方便,这里可以利用 postman 前 ...
- 两种异常(CPU异常、用户模拟异常)的收集
Windows内核分析索引目录:https://www.cnblogs.com/onetrainee/p/11675224.html 两种异常(CPU异常.用户模拟异常)的收集 文章的核心:异常收集 ...
- IOC 概念
转摘:https://www.cnblogs.com/DebugLZQ/archive/2013/06/05/3107957.html 博文目录 1.IOC的理论背景 2.什么是IOC 3.IOC也叫 ...
- Java中Double保留小数位
1.能四舍五入 double d = 114.145; d = (double) Math.round(d * 100) / 100; System.out.println(d); 2. BigDec ...
- 关于join on 和单表查询的实时效果
当数据量大(10W单位级)的时候,join的优势,会被单表查询超过. 以下是两张表单查和两张表联查的时间对比,同时,这样的记录有局限性的. 一.数据量少时: 单表查: 表一:显示行 0 - 2 ( 3 ...
- MySQL为某字段加前缀、后缀
在开发过程中,可能会遇到加前缀或者后缀的情况.比如为视频添加路径时,如果手动加起来肯定慢,而且比较不符合程序员的特点,我们就应该能让程序跑就不会手动加. 使用UPDATE sql 语句:update ...
- synchronized 代码块怎么用
加不加 synchronized 有什么区别? synchronized 作为悲观锁,锁住了什么? 之前 2 篇文章我们已经知道 synchronized 的使用方法以及锁的内容(实例对象和Class ...
- 前端基础进阶(七)-前端工程师最容易出错的问题-this关键字
我们在学习JavaScript的时候,因为对一些概念不是很清楚,但是又会通过一些简洁的方式把它给记下来,那么这样自己记下来的概念和真正的概念产生了很强的偏差. 当然,还有一些以为这个是对的,还会把它发 ...