若是有一天,你的Java程序長時間停頓,也許是它病了,須要用jstack拍個片子分析分析,才能診斷具體什麼病症,是死鎖綜合徵,仍是死循環等其餘病症,本文咱們一塊兒來學習jstack命令~java
jstack是JVM自帶的Java堆棧跟蹤工具,它用於打印出給定的java進程ID、core file、遠程調試服務的Java堆棧信息.git
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 命令格式以下github
jstack [ option ] pid jstack [ option ] executable core jstack [ option ] [server-id@]remote-hostname-or-IP
最經常使用的是服務器
jstack [option] <pid> // 打印某個進程的堆棧信息
option參數說明以下:網絡
選項 | 做用 |
---|---|
-F | 當正常輸出的請求不被響應時,強制輸出線程堆棧 |
-m | 若是調用到本地方法的話,能夠顯示C/C++的堆棧 |
-l | 除堆棧外,顯示關於鎖的附加信息,在發生死鎖時能夠用jstack -l pid來觀察鎖持有狀況 |
jstack用於生成線程快照的,咱們分析線程的狀況,須要複習一下線程狀態吧,拿小凳子坐好,複習一下啦~
多線程
Java語言定義了6種線程池狀態:jvm
Dump文件的線程狀態通常其實就如下3種:jsp
由於Java程序通常都是多線程運行的,Java多線程跟監視鎖環環相扣,因此咱們分析線程狀態時,也須要回顧一下Monitor監視鎖知識。ide
有關於線程同步關鍵字Synchronized與監視鎖的愛恨情仇,有興趣的夥伴能夠看一下我這篇文章
Synchronized解析——若是你願意一層一層剝開個人心工具
Monitor的工做原理圖以下:
死鎖是指兩個或兩個以上的線程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法進行下去。
先來看一段會產生死鎖的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都是隻執行到一半,就陷入了阻塞等待狀態~
經過使用 jps 命令獲取須要監控的進程的pid,咱們找到了23780 DeathLockTest
由上圖,能夠清晰看到死鎖信息:
「mythread-tianluo"線程堆棧信息分析以下:
「mythread-jay"線程堆棧信息分析以下:
來個致使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; } } } } }
在服務器上,咱們能夠經過top命令查看各個進程的cpu使用狀況,它默認是按cpu使用率由高到低排序的
由上圖中,咱們能夠找出pid爲21340的java進程,它佔用了最高的cpu資源,兇手就是它,哈哈!
經過top -Hp 21340能夠查看該進程下,各個線程的cpu使用狀況,以下:
能夠發現pid爲21350的線程,CPU資源佔用最高,嘻嘻,小本本把它記下來,接下來拿jstack給它拍片子
經過top命令定位到cpu佔用率較高的線程以後,接着使用jstack pid命令來查看當前java進程的堆棧狀態,jstack 21350
後,內容以下:
其實,前3個步驟,堆棧信息已經出來啦。可是通常在生成環境,咱們能夠把這些堆棧信息打到一個文件裏,再回頭仔細分析哦~
咱們把佔用cpu資源較高的線程pid(本例子是21350),將該pid轉成16進制的值
在thread dump中,每一個線程都有一個nid,咱們找到對應的nid(5366),發現一直在跑(24行)
這個時候,能夠去檢查代碼是否有問題啦~ 固然,也建議隔段時間再執行一次stack命令,再一份獲取thread dump,畢竟兩次拍片結果(jstack)對比,更準確嘛~