[JVM]JDK自带分析工具jstack简单分析
目录
前言
前面写了jmap 和 visualVM,
jstack在实际生产中也用到过,不过当时都记在印象笔记了~
其实都是总结学习吧,因为之前在职,很多东西没时间写博客。就简单记录在笔记上面了。
这次刷题准备面试,记录博客也是让自己知识更成体系吧。 时间浪费点希望能有更多的价值。
虽然选择高优的事情直接背八股文更加效率,但是我喜欢慢工出细活,欲速则不达。

线上问题
我们查询内容质量校验的时候,
发现,查询hive可能线程卡死了
因为使用线程池做的数据查询。
但是有的日志并不连续,感觉像是没有执行完的样子。
很偶现的事情,后续把日志打印完成,该进行异常处理的都处理,并打印日志,后续就没有发生这个问题了。
jps -lvm
jps -lvm 用于查看当前机器上运行的java进程。
然后用:
jstack -l pid
我们使用 jstack -l 6812 查看我们的应用堆栈信息:
"G1 Concurrent Refinement Thread#11" os_prio=0 tid=0x00007f96b0050800 nid=0x186f runnable
只是看当前堆栈的信息,历史信息并不能看到。
并没法复现现场,因为是0点之后才会做数据质量的校验。
jstack -l > xxxx.jstack
jstack 是一个抓取 thread dump 文件的有效的命令行工具,它位于 JDK 目录里的 bin 文件夹下(JDK_HOMEbin),以下是抓取 dump 文件的命令:
jstack -l >
比如:
jstack -l 6227 >6227.jstack
文件就不放出了,之后可以用fastthread.io分析:[JVM]dump分析工具_fastthread.io
说明
jstack,主要分析线程问题,跟jmap主要分析OOM和堆内存问题不同。
主要用来跟踪java进程中的线程的执行状态,比较重要。
jstack pid; //可以打印出进程中线程的栈,及状态;
作用:
线程发生问题-死锁;
线程状态;
常用参数
localhost:~ bjhl$ jstack -h
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
(to connect to a hung process)
jstack [-m] [-l] <executable> <core>
(to connect to a core file)
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
(to connect to a remote debug server)Options:
-F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message
这边可能会引出,线程的几种状态和生命周期等等
网上的一些使用方式
该部分来自:如何使用jstack分析线程状态
作者:Jessica程序猿
大概就是:
测试环境的服务器cpu使用率一直处于100%,本地又没有什么接口调用,为什么会这样?cpu使用率居高不下,自然是有某些线程一直占用着cpu资源,那又如何查看占用cpu较高的线程?
top命令
在linux环境下,可以通过top命令查看各个进程的cpu使用情况,默认按cpu使用率排序
1、看出pid为23344的java进程占用了较多的cpu资源;
2、通过top -Hp 23344可以查看该进程下各个线程的cpu使用情况;

从进程,找到线程, 然后用jstack:
占了较多的cpu资源,利用jstack命令可以继续查看该线程当前的堆栈状态。
jstack pid
通过top命令定位到cpu占用率较高的线程之后,继续使用jstack pid命令查看当前java进程的堆栈状态

jstack命令生成的thread dump信息包含了JVM中所有存活的线程,为了分析指定线程,必须找出对应线程的调用栈,应该如何找?
在top命令中,已经获取到了占用cpu资源较高的线程pid,将该pid转成16进制的值,在thread dump中每个线程都有一个nid,找到对应的nid即可;隔段时间再执行一次stack命令获取thread dump,区分两份dump是否有差别,在nid=0x246c的线程调用栈中,发现该线程一直在执行JstackCase类第33行的calculate方法,得到这个信息,就可以检查对应的代码是否有问题。
实例1:多线程竞争synchronized锁

很明显:线程1获取到锁,处于RUNNABLE状态,线程2处于BLOCK状态
1、locked <0x000000076bf62208>说明线程1对地址为0x000000076bf62208对象进行了加锁;
2、waiting to lock <0x000000076bf62208>说明线程2在等待地址为0x000000076bf62208对象上的锁;
3、waiting for monitor entry [0x000000001e21f000]说明线程1是通过synchronized关键字进入了监视器的临界区,并处于"Entry Set"队列,等待monitor
实例2:通过wait挂起线程
static class Task implements Runnable {
@Override
public void run() {
synchronized (lock) {
try {
lock.wait();
//TimeUnit.SECONDS.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
dump结果

线程1和2都处于WAITING状态
(这边代码不全,我理解是 用了一个对象当锁,所以都需要等同一个对象。)
1、线程1和2都是先locked <0x000000076bf62500>,再waiting on <0x000000076bf62500>,之所以先锁再等同一个对象,是因为wait方法需要先通过synchronized获得该地址对象的monitor;
2、waiting on <0x000000076bf62500>说明线程执行了wait方法之后,释放了monitor,进入到"Wait Set"队列,等待其它线程执行地址为0x000000076bf62500对象的notify方法,并唤醒自己
(这部分需要多理解一下,Object.wait/notify实现机制,如果只是wait不换型出现什么)
总结
通过top命令定位到cpu占用率较高的线程之后,继续使用jstack pid命令查看当前java进程的堆栈状态(因为jstack pid 只能打印出对应的java进程的所有栈信息,所以需要找到对应的线程,需要转一下16进制)
在top命令中,已经获取到了占用cpu资源较高的线程pid,将该pid转成16进制的值,在thread dump中每个线程都有一个nid,找到对应的nid即可; 可以用来把thread dump的东西分析出来。
然后隔一段时间,在打印一下当前的线程调用栈的信息,查看一下代码是否有问题。
在dump中,线程一般存在如下几种状态:
1、RUNNABLE,线程处于执行中
2、BLOCKED,线程被阻塞
3、WAITING,线程正在等待
jstack命令主要用来查看Java线程的调用堆栈的,可以用来分析线程问题(如死锁)。
jstack用于生成java虚拟机当前时刻的线程快照。
线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。
线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。
如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。
另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。