一、JDK的命令行工具
所有的命令行工具都在bin目錄中。Sun JDK監(jiān)控和故障處理工具如下:
| 名稱 | 主要作用 |
|---|---|
jps |
JVM Process Status Tool,顯示指定系統(tǒng)內所有的HotSpot虛擬機進程 |
jstat |
JVM Statistics Monitoring Tool,用于收集HotSpot虛擬機各方面的運行數(shù)據(jù) |
jinfo |
Configuration Info for Java,顯示虛擬機配置信息 |
jmap |
Memory Map for Java,生成虛擬機的內存轉儲快照(headdump文件) |
jhat |
JVM Heap Dump Browser,用于分析headdump文件,它會建立一個HTTP/HTML服務器,讓用戶可以在瀏覽器上查看分析結果 |
jstack |
Stack Trace for Java,顯示虛擬機的線程快照 |
1.1 jps:虛擬機進程狀況工具
此工具可以列出正在運行的虛擬機進程,并顯示虛擬機執(zhí)行主類(Main Class, main()函數(shù)所在的類)名稱以及這些進程的本地虛擬機唯一ID(Local Virtual Machine Identifier,LVMID)。其他的JDK工具大多需要輸入它查詢到的LVMID來確定要監(jiān)控的是哪一個虛擬機進程。對于本地虛擬機進程來說,LVMID與操作系統(tǒng)的進程ID(Process Identifier,PID)是一致的,使用windows的任務管理器或UNIX的ps命令也可以查詢到虛擬機進程的LVMID,但如果同時啟動了多個虛擬機進程,無法根據(jù)進程名稱定位時,那就只能依賴此命令顯示主類的功能才能區(qū)分了。命令格式如下:
jps [ options ] [ hostid ]
其主要選項如下:
| 選項 | 作用 |
|---|---|
-q |
只輸出LVMID,省略主類的名稱 |
-m |
輸出虛擬機進程啟動時傳遞給主類main()函數(shù)的參數(shù) |
-l |
輸出主類的全名,如果進程執(zhí)行的是Jar包,輸出Jar路徑 |
-v |
輸出虛擬機進程啟動時JVM參數(shù) |
1.2 jstat:虛擬機統(tǒng)計信息監(jiān)視工具
此工具用于監(jiān)視虛擬機各種運行狀態(tài)信息的命令行工具。它可以顯示本地或者遠程虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數(shù)據(jù)。其命令格式為:
jstat [ option vmid [ interval [ s|ms ] [ count ] ] ]
說明:如果是本地虛擬機進程,VMID與LVMID是一致的,如果是遠程虛擬機進程,那VMID的格式應當是:
[ protocol: ][ // ] lvmid [ @hostname [ :port ] / servername ]
其中interval和count代表查詢間隔和次數(shù),如果省略這兩個參數(shù),說明只查詢一次。假設需要250ms查詢一次進程2764垃圾收集狀況,一共查詢20次,那命令應該是:
jstat -gc 2764 250 20
選項option代表著用戶希望查詢的虛擬機信息,主要分為三類:類裝載、垃圾收集、運行期編譯狀況,具體選項及作用如下:
| 選項 | 作用 |
|---|---|
-class |
監(jiān)視類裝載、卸載數(shù)量、總空間以及類裝載所耗費的時間 |
-gc |
監(jiān)視Java堆狀況,包括Eden區(qū)、兩個survivor區(qū)、老年代、永久代等的容量、已用空間、GC時間合計等信息 |
-gccapacity |
監(jiān)視內容與-gc基本相同,但輸出主要關注Java堆各個區(qū)域使用到的最大、最小空間 |
-gcutil |
監(jiān)視內容與-gc基本相同,但輸出主要關注已使用空間占總空間的百分比 |
-gccause |
與-gcutil功能一樣,但是會額外輸出導致上一次GC產(chǎn)生的原因 |
-gcnew |
監(jiān)視新生代GC狀況 |
-gcnewcapacity |
監(jiān)視內容與-gcnew基本相同,輸出主要關注使用到的最大、最小空間 |
-gcold |
監(jiān)視老年代GC狀況 |
-gcoldcapactiy |
監(jiān)視內容與-gcold基本相同,輸出主要關注使用到的最大、最小空間 |
-gcpermcapacity |
輸出永久代使用到的最大、最小空間 |
-compiler |
輸出JIT編譯器編譯過的方法、耗時等信息 |
-printcompilation |
輸出已經(jīng)被JIT編譯的方法 |
下面通過一個例子簡單了解一下:

說明:這臺服務器的新生代
Eden區(qū)(E,表示Eden)使用了6.2%的空間,兩個survivor區(qū)(S0、S1,表示survivor0、survivor1)里面都是空的,老年代(O,表示Old)和永久代(P,表示Permanent)則分別使用了41.42%和47.20%的空間。程序運行以來共發(fā)生Minor GC(YGC,表示Young GC)16次,總耗時0.105s,發(fā)生Full GC(FGC,表示Full GC)三次,Full GC總耗時(FGCT,表示Full GC Time)為0.472s,所有GC總耗時(GCT,表示GC Time)為0.577s。
1.3 jinfo:Java配置信息工具
此工具是實時地查看和調整虛擬機各項參數(shù)。使用jps命令的-v參數(shù)可以查看虛擬機啟動時顯式指定的參數(shù)列表,但如果想知道未被顯式指定的參數(shù)的系統(tǒng)默認值,除了去找資料外,就只能使用jinfo的-flag選項進行查詢了(如果只限于JDK1.6或以上版本,則使用java -XX:+PrintFlagsFinal查看參數(shù)默認值也是一個很好的選擇),jinfo還可以使用-sysprops選項把虛擬機進程的Sytem.getProperties()的內容打印出來,還有在運行期修改參數(shù)的能力,可以使用-flag[+|-] name或-flag name=value修改一部分運行期可寫的虛擬機參數(shù)值。在windows中有較大限制,只提供了最基本的-flag選項。其命令格式如下:
jinfo [ option ] pid
1.4 jmap:Java內存映像工具
此命令用于生成堆轉儲快照(一般稱為headdump或dump文件)。如果不實用此命令,要想獲取Java堆轉儲快照,可以使用之前提到的暴力手段,即使用-XX:+HeadDumpOnOutOfMemoryError參數(shù),可以讓虛擬機在OOM異常出現(xiàn)之后自動生成dump文件,通過-XX:+HeadDumpOnCtrlBreak參數(shù)則可以使用[Ctrl]+[Break]鍵讓虛擬機生成dump文件,又或者在Linux中使用Kill -3命令拿到dump文件。當然,此命令的作用不僅僅是為了獲取dump文件,它還可以查詢finalize執(zhí)行隊列、Java堆和永久代的詳細信息,如空間使用率、當前用的是哪種收集器等。其功能在windows中受限,除了生成dump文件的-dump選項和用于查看每個類的實例、空間占用統(tǒng)計的-histo選項在所有操作系統(tǒng)都提供之外,其余選項都智能在Linux/Solaris下使用。其命令格式:
jmap [ option ] vmid
主要選項如下:
| 選項 | 作用 |
|---|---|
-dump |
生成Java堆轉儲快照。格式為:-dump:[live, ]format=b, file=<filename>, 其中live子參數(shù)說明是否只dump出存活的對象 |
-finalizerinfo |
顯示在F-Queue中等待Finalizer線程執(zhí)行finalizer方法的對象。只在Linux/Solaris平臺有效 |
-heap |
顯示Java堆詳細信息,如使用哪種回收期、參數(shù)配置、分代狀況等。只在Linux/Solaris平臺有效 |
-histo |
顯示堆中對象統(tǒng)計信息,包括類、實例數(shù)量、合計容量 |
-permstat |
以ClassLoader為統(tǒng)計口徑顯示永久代內存狀態(tài)。只在Linux/Solaris平臺有效 |
-F |
當虛擬機進程對-dump選項沒有響應時,可使用這個選項強制生成dump快照。只在Linux/Solaris平臺有效 |
例如:(3500是LVMID)

1.5 jhat:虛擬機堆轉儲快照分析工具
此命令和jmap搭配使用,來分析jmap生成的堆轉儲快照。其內置一個微型的HTTP/HTML服務器,生成dump文件的分析結果后,可以在瀏覽器中查看。一般不會使用此命令來分析dump文件,因為一是一般不會在部署應用的服務器上直接分析dump文件(太耗資源),而是此工具功能相對較為簡陋,可以使用后面講到的VisualVM工具替代。
1.6 jstack:Java堆棧跟蹤工具
此命令用于生成虛擬機當前時刻的線程快照(一般稱為threaddump或者javacore文件)。線程快照就是當前虛擬機內每一條線程正在執(zhí)行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現(xiàn)長時間停頓的原因,如線程間死鎖、死循環(huán)、請求外部資源導致的長時間等待等都是導致線程長時間停頓的常見原因。線程出現(xiàn)停頓的時候通過此命令來查看各個線程的調用堆棧,就可以知道沒有響應的線程到底在后臺做什么,或等待什么資源。其格式為:
jstack [ option ] vmid
主要選項如下:
| 選項 | 作用 |
|---|---|
-F |
當正常輸出的請求不被響應時,強制輸出線程堆棧 |
-l |
除堆棧外,顯示關于鎖的附加信息 |
-m |
如果調用到本地方法的話,可以顯示C/C++的堆棧 |
在JDK1.5中,java.lang.Thread類新增了一個getAllStackTraces()方法用于獲取虛擬機中所有線程的StackTraceElement對象。使用此方法通過幾行簡單的代碼就可以完成jstack的大部分功能,在實際項目中不妨調用此方法做個管理員頁面,隨時查看線程堆棧。

1.7 HSDIS:JIT生成代碼反匯編
隨著技術的發(fā)展,高性能虛擬機真正的細節(jié)實現(xiàn)方式逐漸成了虛擬機實現(xiàn)的“概念模型”——即實現(xiàn)智能保證規(guī)范描述等效?;诖嗽?,在分析程序的執(zhí)行語義問題(虛擬機做了什么)時,在字節(jié)碼層面上分析完全可行,但分析程序的執(zhí)行行為問題(虛擬機是怎樣做的、性能如何)時,在字節(jié)碼層面上分析居沒有什么意義了,需要通過其他方式解決。
HSDIS是官方對劍的HotSpot虛擬機JIT編譯代碼的反匯編插件,它包含在HotSpot虛擬機的源碼之中,但沒有提供編譯后的程序。其作用是讓HotSpot的-XX:+PrintAssembly指令調用它來把動態(tài)生成的本地代碼還原為匯編輸出,同時還生成大量非常有價值的注釋,這樣我們就可以通過輸出的代碼來分析問題。注意,如果使用的Debug或FastDebug版的HotSpot,那可以直接通過-XX:+PrintAssembly指令使用插件;如果使用的是Product版的HotSpot,那還要額外加入一個-XX:+UnlockDiagnosticVMOptions參數(shù)。
例如:測試代碼
public class Bar{
int a = 1;
static int b = 2;
public int sum(int c ){
return a + b + c;
}
public static void main(String[] args){
new Bar().sum(3);
}
}
編譯:
java -XX+:PrintAssembly -Xcomp -XX:CompileCommand=dontinline, *Bar.sum -XX:CompileCommand=Compileonly, *Bar.sum test.Bar
其中-Xcomp是讓虛擬機以編譯模式執(zhí)行代碼,這樣就不需要執(zhí)行足夠次數(shù)來預熱就能觸發(fā)JIT編譯(-Xcomp在新版本HotSpot已被移除)。兩個-XX:CompileCommand是讓編譯器不要內聯(lián)sum()并且只編譯sum(),-XX+:PrintAssembly就是輸出反匯編內容。如果一切順利會得到如下代碼:
[Disassembling for mach='i386']
[Entry Point]
[Constants]
# {method} 'sum' '(I)I' in 'test/Bar'
# this: ecx = 'test/Bar'
# parm0: edx = int
# [sp+0x20] (sp of caller)
……
0x01cac407: cmp 0x4(%ecx),%eax
0x01cac40a: jne 0x01c6b050 ; {runtime_call}
[Verified Entry Point]
0x01cac410: mov %eax,-0x8000(%esp)
0x01cac417: push %ebp
0x01cac418: sub $0x18,%esp ;*aload_0
; - test.Bar::sum@0 (line 8)
;; block B0 [0, 10]
0x01cac41b: mov 0x8(%ecx),%eax ;*getfield a
; - test.Bar::sum@1 (line 8)
0x01cac41e: mov $0x3d2fad8,%esi ; {oop(a
'java/lang/Class' = 'test/Bar')}
0x01cac423: mov 0x68(%esi),%esi ;*getstatic b
; - test.Bar::sum@4 (line 8)
0x01cac426: add %esi,%eax
0x01cac428: add %edx,%eax
0x01cac42a: add $0x18,%esp
0x01cac42d: pop %ebp
0x01cac42e: test %eax,0x2b0100 ; {poll_return}
0x01cac434: ret
說明:
- 1、
mov %eax,-0x8000(%esp):檢查棧溢。 - 2、
push %ebp:保存上一棧幀基址。 - 3、
sub $0x18,%esp:給新幀分配空間。 - 4、
mov 0x8(%ecx),%eax:取實例變量a,這里0x8(%ecx)就是ecx+0x8的意思,前面“[Constants]”節(jié)中提示了“this:ecx = 'test/Bar'”,即ecx寄存器中放的就是this對象的地址。偏移0x8是越過this對象的對象頭,之后就是實例變量a的內存位置。這次是訪問“Java堆”中的數(shù)據(jù)。 - 5、
mov $0x3d2fad8,%esi:取test.Bar在方法區(qū)的指針。 - 6、
mov 0x68(%esi),%esi:取類變量b,這次是訪問“方法區(qū)”中的數(shù)據(jù)。 - 7、
add %esi,%eax 、add %edx,%eax:做2次加法,求a+b+c的值,前面的代碼把a放在eax中,把b放在esi中,而c在[Constants]中提示了,“parm0:edx = int”,說明c在edx中。 - 8、
add $0x18,%esp:撤銷棧幀。 - 9、
pop %ebp:恢復上一棧幀。 - 10、
test %eax,0x2b0100:輪詢方法返回處的SafePoint - 11、
ret:方法返回。