在故障定位(尤其是out of memory)和性能分析的時候,經常會用到一些文件來幫助我們排除代碼問題。這些文件記錄了JVM運行期間的內存占用、線程執行等情況,這就是我們常說的dump文件。常用的有heap dump和thread dump(也叫javacore,或java dump)。我們可以這么理解:heap dump記錄內存信息的,thread dump是記錄CPU信息的。
heap dump:
heap dump文件是一個二進制文件,它保存了某一時刻JVM堆中對象使用情況。HeapDump文件是指定時刻的Java堆棧的快照,是一種鏡像文件。Heap Analyzer工具通過分析HeapDump文件,哪些對象占用了太多的堆棧空間,來發現導致內存泄露或者可能引起內存泄露的對象。
thread dump:
thread dump文件主要保存的是java應用中各線程在某一時刻的運行的位置,即執行到哪一個類的哪一個方法哪一個行上。thread dump是一個文本文件,打開后可以看到每一個線程的執行棧,以stacktrace的方式顯示。通過對thread dump的分析可以得到應用是否“卡”在某一點上,即在某一點運行的時間太長,如數據庫查詢,長期得不到響應,最終導致系統崩潰。單個的thread dump文件一般來說是沒有什么用處的,因為它只是記錄了某一個絕對時間點的情況。比較有用的是,線程在一個時間段內的執行情況。
兩個thread dump文件在分析時特別有效,困為它可以看出在先后兩個時間點上,線程執行的位置,如果發現先后兩組數據中同一線程都執行在同一位置,則說明此處可能有問題,因為程序運行是極快的,如果兩次均在某一點上,說明這一點的耗時是很大的。通過對這兩個文件進行分析,查出原因,進而解決問題。
threadDump導出幾種方式:
1、在類Linux系統,通過kill -3 <pid>導出
2、通過ps -ef或者jps找到pid,再通過jstack -l pid > a.tdump
3、通過jvisualvm或者jprofiler等可視化工具直接生成線程dump
案例一:下面分析一段代碼產生死鎖,以及如何分析
public class DeadLockDemo { public static void main(String[] args) { Object a = new Object(); Object b = new Object(); new Thread(()->{ synchronized (a){ try { System.out.println(Thread.currentThread().getName() + ",locked a"); Thread.sleep(3000); synchronized (b){ System.out.println(Thread.currentThread().getName() + ",locked b"); } } catch (InterruptedException e) { e.printStackTrace(); } } },"threadA").start(); new Thread(()->{ synchronized (b){ try { System.out.println(Thread.currentThread().getName() + ",locked b"); Thread.sleep(3000); synchronized (a){ System.out.println(Thread.currentThread().getName() + ",locked a"); } } catch (InterruptedException e) { e.printStackTrace(); } } },"threadB").start(); } }
執行結果:
threadA,locked a
threadB,locked b
分析步驟:
1、通過jps -l(只能查看到本機java進程)或者ps -ef | grep ...(可以查看到所有用戶的java進程),找到pid
2、jstack -l pid(內容較多的話可以重定向到一個文件,jstack -l pid > result.tdump)可以看到死鎖很明顯found 1 deadlock。
Java stack information for the threads listed above: =================================================== "threadB": at cn.htd.DeadLockDemo.lambda$main$1(DeadLockDemo.java:27) - waiting to lock <0x000000076c1b6860> (a java.lang.Object) - locked <0x000000076c1b6870> (a java.lang.Object) at cn.htd.DeadLockDemo$$Lambda$2/1452126962.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) "threadA": at cn.htd.DeadLockDemo.lambda$main$0(DeadLockDemo.java:13) - waiting to lock <0x000000076c1b6870> (a java.lang.Object) - locked <0x000000076c1b6860> (a java.lang.Object) at cn.htd.DeadLockDemo$$Lambda$1/81628611.run(Unknown Source) at java.lang.Thread.run(Thread.java:748) Found 1 deadlock.
案例二:定位cpu占用率高的java線程
1、通過top查找到cpu占用率高的java進程。
2、定位最耗CPU的線程,命令:top -Hp 11721
3、把線程id轉為16進制,通過jstack打印線程堆棧信息,就可以定位問題代碼
命令: jstack -l 11721 | grep $(printf "%x\n" 12687) -50 --定位出找到的nid以及前面后面各50行
命令2:jstack -l 11721 | grep $(printf "%x\n" 12687) -A50 --定位出找到的nid以及后面50行
命令3:jstack -l 11721 | grep $(printf "%x\n" 12687) -B50 --定位出找到的nid以及前面50行