jvm系列(四):jvm調(diào)優(yōu)-命令篇
運用jvm自帶的命令可以方便的在生產(chǎn)監(jiān)控和打印堆棧的日志信息幫忙我們來定位問題!雖然jvm調(diào)優(yōu)成熟的工具已經(jīng)有很多:jconsole、大名鼎鼎的VisualVM,IBM的Memory Analyzer等等,但是在生產(chǎn)環(huán)境出現(xiàn)問題的時候,一方面工具的使用會有所限制,另一方面喜歡裝X的我們,總喜歡在出現(xiàn)問題的時候在終端輸入一些命令來解決。所有的工具幾乎都是依賴于jdk的接口和底層的這些命令,研究這些命令的使用也讓我們更能了解jvm構(gòu)成和特性。
Sun JDK監(jiān)控和故障處理命令有jps jstat jmap jhat jstack jinfo下面做一一介紹
jps
JVM Process Status Tool,顯示指定系統(tǒng)內(nèi)所有的HotSpot虛擬機進程。
命令格式
- jps [options] [hostid]
 
option參數(shù)
- -l : 輸出主類全名或jar路徑
 - -q : 只輸出LVMID
 - -m : 輸出JVM啟動時傳遞給main()的參數(shù)
 - -v : 輸出JVM啟動時顯示指定的JVM參數(shù)
 
其中[option]、[hostid]參數(shù)也可以不寫。
示例
- $ jps -l -m
 - 28920 org.apache.catalina.startup.Bootstrap start
 - 11589 org.apache.catalina.startup.Bootstrap start
 - 25816 sun.tools.jps.Jps -l -m
 
jstat
jstat(JVM statistics Monitoring)是用于監(jiān)視虛擬機運行時狀態(tài)信息的命令,它可以顯示出虛擬機進程中的類裝載、內(nèi)存、垃圾收集、JIT編譯等運行數(shù)據(jù)。
命令格式
- jstat [option] LVMID [interval] [count]
 
參數(shù)
option 參數(shù)詳解
-class
監(jiān)視類裝載、卸載數(shù)量、總空間以及耗費的時間
- $ jstat -class 11589
 - Loaded Bytes Unloaded Bytes Time
 - 7035 14506.3 0 0.0 3.67
 
- Loaded : 加載class的數(shù)量
 - Bytes : class字節(jié)大小
 - Unloaded : 未加載class的數(shù)量
 - Bytes : 未加載class的字節(jié)大小
 - Time : 加載時間
 
-compiler
輸出JIT編譯過的方法數(shù)量耗時等
- $ jstat -gccapacity 1262
 - NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC PGCMN PGCMX PGC PC YGC FGC
 - 614400.0 614400.0 614400.0 26112.0 24064.0 564224.0 434176.0 434176.0 434176.0 434176.0 524288.0 1048576.0 524288.0 524288.0 320 1
 
- Compiled : 編譯數(shù)量
 - Failed : 編譯失敗數(shù)量
 - Invalid : 無效數(shù)量
 - Time : 編譯耗時
 - FailedType : 失敗類型
 - FailedMethod : 失敗方法的全限定名
 
-gc
垃圾回收堆的行為統(tǒng)計,常用命令
- $ jstat -gc 1262
 - S0C S1C S0U S1U EC EU OC OU PC PU YGC YGCT FGC FGCT GCT
 - 26112.0 24064.0 6562.5 0.0 564224.0 76274.5 434176.0 388518.3 524288.0 42724.7 320 6.417 1 0.398 6.815
 
C即Capacity 總?cè)萘?,U即Used 已使用的容量
- S0C : survivor0區(qū)的總?cè)萘?/li>
 - S1C : survivor1區(qū)的總?cè)萘?/li>
 - S0U : survivor0區(qū)已使用的容量
 - S1C : survivor1區(qū)已使用的容量
 - EC : Eden區(qū)的總?cè)萘?/li>
 - EU : Eden區(qū)已使用的容量
 - OC : Old區(qū)的總?cè)萘?/li>
 - OU : Old區(qū)已使用的容量
 - PC 當前perm的容量 (KB)
 - PU perm的使用 (KB)
 - YGC : 新生代垃圾回收次數(shù)
 - YGCT : 新生代垃圾回收時間
 - FGC : 老年代垃圾回收次數(shù)
 - FGCT : 老年代垃圾回收時間
 - GCT : 垃圾回收總消耗時間
 
- $ jstat -gc 1262 2000 20
 
這個命令意思就是每隔2000ms輸出1262的gc情況,一共輸出20次
-gccapacity
同-gc,不過還會輸出Java堆各區(qū)域使用到的***、最小空間
- $ jstat -gccapacity 1262
 - NGCMN NGCMX NGC S0C S1C EC OGCMN OGCMX OGC OC PGCMN PGCMX PGC PC YGC FGC
 - 614400.0 614400.0 614400.0 26112.0 24064.0 564224.0 434176.0 434176.0 434176.0 434176.0 524288.0 1048576.0 524288.0 524288.0 320 1
 
- NGCMN : 新生代占用的最小空間
 - NGCMX : 新生代占用的***空間
 - OGCMN : 老年代占用的最小空間
 - OGCMX : 老年代占用的***空間
 - OGC:當前年老代的容量 (KB)
 - OC:當前年老代的空間 (KB)
 - PGCMN : perm占用的最小空間
 - PGCMX : perm占用的***空間
 
-gcutil
同-gc,不過輸出的是已使用空間占總空間的百分比
- $ jstat -gcutil 28920
 - S0 S1 E O P YGC YGCT FGC FGCT GCT
 - 12.45 0.00 33.85 0.00 4.44 4 0.242 0 0.000 0.242
 
-gccause
垃圾收集統(tǒng)計概述(同-gcutil),附加最近兩次垃圾回收事件的原因
- $ jstat -gccause 28920
 - S0 S1 E O P YGC YGCT FGC FGCT GCT LGCC GCC
 - 12.45 0.00 33.85 0.00 4.44 4 0.242 0 0.000 0.242 Allocation Failure No GC
 
- LGCC:最近垃圾回收的原因
 - GCC:當前垃圾回收的原因
 
-gcnew
統(tǒng)計新生代的行為
- $ jstat -gcnew 28920
 - S0C S1C S0U S1U TT MTT DSS EC EU YGC YGCT
 - 419392.0 419392.0 52231.8 0.0 6 6 209696.0 3355520.0 1172246.0 4 0.242
 
- TT:Tenuring threshold(提升閾值)
 - MTT:***的tenuring threshold
 - DSS:survivor區(qū)域大小 (KB)
 
-gcnewcapacity
新生代與其相應的內(nèi)存空間的統(tǒng)計
- $ jstat -gcnewcapacity 28920
 - NGCMN NGCMX NGC S0CMX S0C S1CMX S1C ECMX EC YGC FGC
 - 4194304.0 4194304.0 4194304.0 419392.0 419392.0 419392.0 419392.0 3355520.0 3355520.0 4 0
 
- NGC:當前年輕代的容量 (KB)
 - S0CMX:***的S0空間 (KB)
 - S0C:當前S0空間 (KB)
 - ECMX:***eden空間 (KB)
 - EC:當前eden空間 (KB)
 
-gcold
統(tǒng)計舊生代的行為
- $ jstat -gcold 28920
 - PC PU OC OU YGC FGC FGCT GCT
 - 1048576.0 46561.7 6291456.0 0.0 4 0 0.000 0.242
 
-gcoldcapacity
統(tǒng)計舊生代的大小和空間
- $ jstat -gcoldcapacity 28920
 - OGCMN OGCMX OGC OC YGC FGC FGCT GCT
 - 6291456.0 6291456.0 6291456.0 6291456.0 4 0 0.000 0.242
 
-gcpermcapacity
永生代行為統(tǒng)計
- $ jstat -gcpermcapacity 28920
 - PGCMN PGCMX PGC PC YGC FGC FGCT GCT
 - 1048576.0 2097152.0 1048576.0 1048576.0 4 0 0.000 0.242
 
-printcompilation
hotspot編譯方法統(tǒng)計
- $ jstat -printcompilation 28920
 - Compiled Size Type Method
 - 1291 78 1 java/util/ArrayList indexOf
 
- Compiled:被執(zhí)行的編譯任務的數(shù)量
 - Size:方法字節(jié)碼的字節(jié)數(shù)
 - Type:編譯類型
 - Method:編譯方法的類名和方法名。類名使用"/" 代替 "." 作為空間分隔符. 方法名是給出類的方法名. 格式是一致于HotSpot - XX:+PrintComplation 選項
 
jmap
jmap(JVM Memory Map)命令用于生成heap dump文件,如果不使用這個命令,還闊以使用-XX:+HeapDumpOnOutOfMemoryError參數(shù)來讓虛擬機出現(xiàn)OOM的時候·自動生成dump文件。 jmap不僅能生成dump文件,還闊以查詢finalize執(zhí)行隊列、Java堆和***代的詳細信息,如當前使用率、當前使用的是哪種收集器等。
命令格式
- jmap [option] LVMID
 
option參數(shù)
- dump : 生成堆轉(zhuǎn)儲快照
 - finalizerinfo : 顯示在F-Queue隊列等待Finalizer線程執(zhí)行finalizer方法的對象
 - heap : 顯示Java堆詳細信息
 - histo : 顯示堆中對象的統(tǒng)計信息
 - permstat : to print permanent generation statistics
 - F : 當-dump沒有響應時,強制生成dump快照
 
示例
-dump
常用格式
- -dump::live,format=b,file=<filename> pid
 
dump堆到文件,format指定輸出格式,live指明是活著的對象,file指定文件名
- $ jmap -dump:live,format=b,file=dump.hprof 28920
 - Dumping heap to /home/xxx/dump.hprof ...
 - Heap dump file created
 
dump.hprof這個后綴是為了后續(xù)可以直接用MAT(Memory Anlysis Tool)打開。
-finalizerinfo
打印等待回收對象的信息
- $ jmap -finalizerinfo 28920
 - Attaching to process ID 28920, please wait...
 - Debugger attached successfully.
 - Server compiler detected.
 - JVM version is 24.71-b01
 - Number of objects pending for finalization: 0
 
可以看到當前F-QUEUE隊列中并沒有等待Finalizer線程執(zhí)行finalizer方法的對象。
-heap
打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情況,可以用此來判斷內(nèi)存目前的使用情況以及垃圾回收情況
- $ jmap -heap 28920
 - Attaching to process ID 28920, please wait...
 - Debugger attached successfully.
 - Server compiler detected.
 - JVM version is 24.71-b01
 - using thread-local object allocation.
 - Parallel GC with 4 thread(s)//GC 方式
 - Heap Configuration: //堆內(nèi)存初始化配置
 - MinHeapFreeRatio = 0 //對應jvm啟動參數(shù)-XX:MinHeapFreeRatio設(shè)置JVM堆最小空閑比率(default 40)
 - MaxHeapFreeRatio = 100 //對應jvm啟動參數(shù) -XX:MaxHeapFreeRatio設(shè)置JVM堆***空閑比率(default 70)
 - MaxHeapSize = 2082471936 (1986.0MB) //對應jvm啟動參數(shù)-XX:MaxHeapSize=設(shè)置JVM堆的***大小
 - NewSize = 1310720 (1.25MB)//對應jvm啟動參數(shù)-XX:NewSize=設(shè)置JVM堆的‘新生代’的默認大小
 - MaxNewSize = 17592186044415 MB//對應jvm啟動參數(shù)-XX:MaxNewSize=設(shè)置JVM堆的‘新生代’的***大小
 - OldSize = 5439488 (5.1875MB)//對應jvm啟動參數(shù)-XX:OldSize=<value>:設(shè)置JVM堆的‘老生代’的大小
 - NewRatio = 2 //對應jvm啟動參數(shù)-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
 - SurvivorRatio = 8 //對應jvm啟動參數(shù)-XX:SurvivorRatio=設(shè)置年輕代中Eden區(qū)與Survivor區(qū)的大小比值
 - PermSize = 21757952 (20.75MB) //對應jvm啟動參數(shù)-XX:PermSize=<value>:設(shè)置JVM堆的‘永生代’的初始大小
 - MaxPermSize = 85983232 (82.0MB)//對應jvm啟動參數(shù)-XX:MaxPermSize=<value>:設(shè)置JVM堆的‘永生代’的***大小
 - G1HeapRegionSize = 0 (0.0MB)
 - Heap Usage://堆內(nèi)存使用情況
 - PS Young Generation
 - Eden Space://Eden區(qū)內(nèi)存分布
 - capacity = 33030144 (31.5MB)//Eden區(qū)總?cè)萘?nbsp;
 - used = 1524040 (1.4534378051757812MB) //Eden區(qū)已使用
 - free = 31506104 (30.04656219482422MB) //Eden區(qū)剩余容量
 - 4.614088270399305% used //Eden區(qū)使用比率
 - From Space: //其中一個Survivor區(qū)的內(nèi)存分布
 - capacity = 5242880 (5.0MB)
 - used = 0 (0.0MB)
 - free = 5242880 (5.0MB)
 - 0.0% used
 - To Space: //另一個Survivor區(qū)的內(nèi)存分布
 - capacity = 5242880 (5.0MB)
 - used = 0 (0.0MB)
 - free = 5242880 (5.0MB)
 - 0.0% used
 - PS Old Generation //當前的Old區(qū)內(nèi)存分布
 - capacity = 86507520 (82.5MB)
 - used = 0 (0.0MB)
 - free = 86507520 (82.5MB)
 - 0.0% used
 - PS Perm Generation//當前的 “永生代” 內(nèi)存分布
 - capacity = 22020096 (21.0MB)
 - used = 2496528 (2.3808746337890625MB)
 - free = 19523568 (18.619125366210938MB)
 - 11.337498256138392% used
 - 670 interned Strings occupying 43720 bytes.
 
可以很清楚的看到Java堆中各個區(qū)域目前的情況。
-histo
打印堆的對象統(tǒng)計,包括對象數(shù)、內(nèi)存大小等等 (因為在dump:live前會進行full gc,如果帶上live則只統(tǒng)計活對象,因此不加live的堆大小要大于加live堆的大小 )
- $ jmap -histo:live 28920 | more
 - num #instances #bytes class name
 - ----------------------------------------------
 - 1: 83613 12012248 <constMethodKlass>
 - 2: 23868 11450280 [B
 - 3: 83613 10716064 <methodKlass>
 - 4: 76287 10412128 [C
 - 5: 8227 9021176 <constantPoolKlass>
 - 6: 8227 5830256 <instanceKlassKlass>
 - 7: 7031 5156480 <constantPoolCacheKlass>
 - 8: 73627 1767048 java.lang.String
 - 9: 2260 1348848 <methodDataKlass>
 - 10: 8856 849296 java.lang.Class
 
僅僅打印了前10行
xml class name是對象類型,說明如下:
- B byte
 - C char
 - D double
 - F float
 - I int
 - J long
 - Z boolean
 - [ 數(shù)組,如[I表示int[]
 - [L+類名 其他對象
 
-permstat
打印Java堆內(nèi)存的***保存區(qū)域的類加載器的智能統(tǒng)計信息。對于每個類加載器而言,它的名稱、活躍度、地址、父類加載器、它所加載的類的數(shù)量和大小都會被打印。此外,包含的字符串數(shù)量和大小也會被打印。
- $ jmap -permstat 28920
 - Attaching to process ID 28920, please wait...
 - Debugger attached successfully.
 - Server compiler detected.
 - JVM version is 24.71-b01
 - finding class loader instances ..done.
 - computing per loader stat ..done.
 - please wait.. computing liveness.liveness analysis may be inaccurate ...
 - class_loader classes bytes parent_loader alive? type
 - <bootstrap> 3111 18154296 null live <internal>
 - 0x0000000600905cf8 1 1888 0x0000000600087f08 dead sun/reflect/DelegatingClassLoader@0x00000007800500a0
 - 0x00000006008fcb48 1 1888 0x0000000600087f08 dead sun/reflect/DelegatingClassLoader@0x00000007800500a0
 - 0x00000006016db798 0 0 0x00000006008d3fc0 dead java/util/ResourceBundle$RBClassLoader@0x0000000780626ec0
 - 0x00000006008d6810 1 3056 null dead sun/reflect/DelegatingClassLoader@0x00000007800500a0
 
-F
強制模式。如果指定的pid沒有響應,請使用jmap -dump或jmap -histo選項。此模式下,不支持live子選項。
jhat
jhat(JVM Heap Analysis Tool)命令是與jmap搭配使用,用來分析jmap生成的dump,jhat內(nèi)置了一個微型的HTTP/HTML服務器,生成dump的分析結(jié)果后,可以在瀏覽器中查看。在此要注意,一般不會直接在服務器上進行分析,因為jhat是一個耗時并且耗費硬件資源的過程,一般把服務器生成的dump文件復制到本地或其他機器上進行分析。
命令格式
- jhat [dumpfile]
 
參數(shù)
- -stack false|true 關(guān)閉對象分配調(diào)用棧跟蹤(tracking object allocation call stack)。 如果分配位置信息在堆轉(zhuǎn)儲中不可用. 則必須將此標志設(shè)置為 false. 默認值為 true.>
 - -refs false|true 關(guān)閉對象引用跟蹤(tracking of references to objects)。 默認值為 true. 默認情況下, 返回的指針是指向其他特定對象的對象,如反向鏈接或輸入引用(referrers or incoming references), 會統(tǒng)計/計算堆中的所有對象。>
 - -port port-number 設(shè)置 jhat HTTP server 的端口號. 默認值 7000.>
 - -exclude exclude-file 指定對象查詢時需要排除的數(shù)據(jù)成員列表文件(a file that lists data members that should be excluded from the reachable objects query)。 例如, 如果文件列列出了 java.lang.String.value , 那么當從某個特定對象 Object o 計算可達的對象列表時, 引用路徑涉及 java.lang.String.value 的都會被排除。>
 - -baseline exclude-file 指定一個基準堆轉(zhuǎn)儲(baseline heap dump)。 在兩個 heap dumps 中有相同 object ID 的對象會被標記為不是新的(marked as not being new). 其他對象被標記為新的(new). 在比較兩個不同的堆轉(zhuǎn)儲時很有用.>
 - -debug int 設(shè)置 debug 級別. 0 表示不輸出調(diào)試信息。 值越大則表示輸出更詳細的 debug 信息.>
 - -version 啟動后只顯示版本信息就退出>
 - -J< flag > 因為 jhat 命令實際上會啟動一個JVM來執(zhí)行, 通過 -J 可以在啟動JVM時傳入一些啟動參數(shù). 例如, -J-Xmx512m 則指定運行 jhat 的Java虛擬機使用的***堆內(nèi)存為 512 MB. 如果需要使用多個JVM啟動參數(shù),則傳入多個 -Jxxxxxx.
 
示例
- $ jhat -J-Xmx512m dump.hprof
 - eading from dump.hprof...
 - Dump file created Fri Mar 11 17:13:42 CST 2016
 - Snapshot read, resolving...
 - Resolving 271678 objects...
 - Chasing references, expect 54 dots......................................................
 - Eliminating duplicate references......................................................
 - Snapshot resolved.
 - Started HTTP server on port 7000
 - Server is ready.
 
中間的-J-Xmx512m是在dump快照很大的情況下分配512M內(nèi)存去啟動HTTP服務器,運行完之后就可在瀏覽器打開Http://localhost:7000進行快照分析 堆快照分析主要在***面的Heap Histogram里,里面根據(jù)class列出了dump的時候所有存活對象。
分析同樣一個dump快照,MAT需要的額外內(nèi)存比jhat要小的多的多,所以建議使用MAT來進行分析,當然也看個人偏好。
分析
打開瀏覽器Http://localhost:7000,該頁面提供了幾個查詢功能可供使用:
- All classes including platform
 - Show all members of the rootset
 - Show instance counts for all classes (including platform)
 - Show instance counts for all classes (excluding platform)
 - Show heap histogram
 - Show finalizer summary
 - Execute Object Query Language (OQL) query
 
一般查看堆異常情況主要看這個兩個部分: Show instance counts for all classes (excluding platform),平臺外的所有對象信息。如下圖:

Show heap histogram 以樹狀圖形式展示堆情況。如下圖:

具體排查時需要結(jié)合代碼,觀察是否大量應該被回收的對象在一直被引用或者是否有占用內(nèi)存特別大的對象無法被回收。
一般情況,會down到客戶端用工具來分析
jstack
jstack用于生成java虛擬機當前時刻的線程快照。線程快照是當前java虛擬機內(nèi)每一條線程正在執(zhí)行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現(xiàn)長時間停頓的原因,如線程間死鎖、死循環(huán)、請求外部資源導致的長時間等待等。 線程出現(xiàn)停頓的時候通過jstack來查看各個線程的調(diào)用堆棧,就可以知道沒有響應的線程到底在后臺做什么事情,或者等待什么資源。 如果java程序崩潰生成core文件,jstack工具可以用來獲得core文件的java stack和native stack的信息,從而可以輕松地知道java程序是如何崩潰和在程序何處發(fā)生問題。另外,jstack工具還可以附屬到正在運行的java程序中,看到當時運行的java程序的java stack和native stack的信息, 如果現(xiàn)在運行的java程序呈現(xiàn)hung的狀態(tài),jstack是非常有用的。
命令格式
- jstack [option] LVMID
 
option參數(shù)
- -F : 當正常輸出請求不被響應時,強制輸出線程堆棧
 - -l : 除堆棧外,顯示關(guān)于鎖的附加信息
 - -m : 如果調(diào)用到本地方法的話,可以顯示C/C++的堆棧
 
示例
- $ jstack -l 11494|more
 - 2016-07-28 13:40:04
 - Full thread dump Java HotSpot(TM) 64-Bit Server VM (24.71-b01 mixed mode):
 - "Attach Listener" daemon prio=10 tid=0x00007febb0002000 nid=0x6b6f waiting on condition [0x0000000000000000]
 - java.lang.Thread.State: RUNNABLE
 - Locked ownable synchronizers:
 - - None
 - "http-bio-8005-exec-2" daemon prio=10 tid=0x00007feb94028000 nid=0x7b8c waiting on condition [0x00007fea8f56e000]
 - java.lang.Thread.State: WAITING (parking)
 - at sun.misc.Unsafe.park(Native Method)
 - - parking to wait for <0x00000000cae09b80> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
 - at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
 - at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
 - at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
 - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
 - at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
 - at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
 - at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
 - at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
 - at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
 - at java.lang.Thread.run(Thread.java:745)
 - Locked ownable synchronizers:
 - - None
 - .....
 
分析
這里有一篇文章解釋的很好 分析打印出的文件內(nèi)容
jinfo
jinfo(JVM Configuration info)這個命令作用是實時查看和調(diào)整虛擬機運行參數(shù)。 之前的jps -v口令只能查看到顯示指定的參數(shù),如果想要查看未被顯示指定的參數(shù)的值就要使用jinfo口令
命令格式
- jinfo [option] [args] LVMID
 
option參數(shù)
- -flag : 輸出指定args參數(shù)的值
 - -flags : 不需要args參數(shù),輸出所有JVM參數(shù)的值
 - -sysprops : 輸出系統(tǒng)屬性,等同于System.getProperties()
 
示例
- $ jinfo -flag 11494
 - -XX:CMSInitiatingOccupancyFraction=80
 
【本文為51CTO專欄作者“純潔的微笑”的原創(chuàng)稿件,轉(zhuǎn)載請通過微信公眾號聯(lián)系作者獲取授權(quán)】


















 
 
 





 
 
 
 