jvm系列(四):jvm調優-命令篇
運用jvm自帶的命令可以方便的在生產監控和打印堆棧的日志信息幫忙我們來定位問題!雖然jvm調優成熟的工具已經有很多:jconsole、大名鼎鼎的VisualVM,IBM的Memory Analyzer等等,但是在生產環境出現問題的時候,一方面工具的使用會有所限制,另一方面喜歡裝X的我們,總喜歡在出現問題的時候在終端輸入一些命令來解決。所有的工具幾乎都是依賴于jdk的接口和底層的這些命令,研究這些命令的使用也讓我們更能了解jvm構成和特性。
Sun JDK監控和故障處理命令有jps jstat jmap jhat jstack jinfo下面做一一介紹
jps
JVM Process Status Tool,顯示指定系統內所有的HotSpot虛擬機進程。
命令格式
- jps [options] [hostid]
option參數
- -l : 輸出主類全名或jar路徑
- -q : 只輸出LVMID
- -m : 輸出JVM啟動時傳遞給main()的參數
- -v : 輸出JVM啟動時顯示指定的JVM參數
其中[option]、[hostid]參數也可以不寫。
示例
- $ 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)是用于監視虛擬機運行時狀態信息的命令,它可以顯示出虛擬機進程中的類裝載、內存、垃圾收集、JIT編譯等運行數據。
命令格式
- jstat [option] LVMID [interval] [count]
參數
option 參數詳解
-class
監視類裝載、卸載數量、總空間以及耗費的時間
- $ jstat -class 11589
- Loaded Bytes Unloaded Bytes Time
- 7035 14506.3 0 0.0 3.67
- Loaded : 加載class的數量
- Bytes : class字節大小
- Unloaded : 未加載class的數量
- Bytes : 未加載class的字節大小
- Time : 加載時間
-compiler
輸出JIT編譯過的方法數量耗時等
- $ 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 : 編譯數量
- Failed : 編譯失敗數量
- Invalid : 無效數量
- Time : 編譯耗時
- FailedType : 失敗類型
- FailedMethod : 失敗方法的全限定名
-gc
垃圾回收堆的行為統計,常用命令
- $ 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 總容量,U即Used 已使用的容量
- S0C : survivor0區的總容量
- S1C : survivor1區的總容量
- S0U : survivor0區已使用的容量
- S1C : survivor1區已使用的容量
- EC : Eden區的總容量
- EU : Eden區已使用的容量
- OC : Old區的總容量
- OU : Old區已使用的容量
- PC 當前perm的容量 (KB)
- PU perm的使用 (KB)
- YGC : 新生代垃圾回收次數
- YGCT : 新生代垃圾回收時間
- FGC : 老年代垃圾回收次數
- FGCT : 老年代垃圾回收時間
- GCT : 垃圾回收總消耗時間
- $ jstat -gc 1262 2000 20
這個命令意思就是每隔2000ms輸出1262的gc情況,一共輸出20次
-gccapacity
同-gc,不過還會輸出Java堆各區域使用到的***、最小空間
- $ 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
垃圾收集統計概述(同-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
統計新生代的行為
- $ 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區域大小 (KB)
-gcnewcapacity
新生代與其相應的內存空間的統計
- $ 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
統計舊生代的行為
- $ 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
統計舊生代的大小和空間
- $ 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
永生代行為統計
- $ 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編譯方法統計
- $ jstat -printcompilation 28920
- Compiled Size Type Method
- 1291 78 1 java/util/ArrayList indexOf
- Compiled:被執行的編譯任務的數量
- Size:方法字節碼的字節數
- Type:編譯類型
- Method:編譯方法的類名和方法名。類名使用"/" 代替 "." 作為空間分隔符. 方法名是給出類的方法名. 格式是一致于HotSpot - XX:+PrintComplation 選項
jmap
jmap(JVM Memory Map)命令用于生成heap dump文件,如果不使用這個命令,還闊以使用-XX:+HeapDumpOnOutOfMemoryError參數來讓虛擬機出現OOM的時候·自動生成dump文件。 jmap不僅能生成dump文件,還闊以查詢finalize執行隊列、Java堆和***代的詳細信息,如當前使用率、當前使用的是哪種收集器等。
命令格式
- jmap [option] LVMID
option參數
- dump : 生成堆轉儲快照
- finalizerinfo : 顯示在F-Queue隊列等待Finalizer線程執行finalizer方法的對象
- heap : 顯示Java堆詳細信息
- histo : 顯示堆中對象的統計信息
- 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這個后綴是為了后續可以直接用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線程執行finalizer方法的對象。
-heap
打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情況,可以用此來判斷內存目前的使用情況以及垃圾回收情況
- $ 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: //堆內存初始化配置
- MinHeapFreeRatio = 0 //對應jvm啟動參數-XX:MinHeapFreeRatio設置JVM堆最小空閑比率(default 40)
- MaxHeapFreeRatio = 100 //對應jvm啟動參數 -XX:MaxHeapFreeRatio設置JVM堆***空閑比率(default 70)
- MaxHeapSize = 2082471936 (1986.0MB) //對應jvm啟動參數-XX:MaxHeapSize=設置JVM堆的***大小
- NewSize = 1310720 (1.25MB)//對應jvm啟動參數-XX:NewSize=設置JVM堆的‘新生代’的默認大小
- MaxNewSize = 17592186044415 MB//對應jvm啟動參數-XX:MaxNewSize=設置JVM堆的‘新生代’的***大小
- OldSize = 5439488 (5.1875MB)//對應jvm啟動參數-XX:OldSize=<value>:設置JVM堆的‘老生代’的大小
- NewRatio = 2 //對應jvm啟動參數-XX:NewRatio=:‘新生代’和‘老生代’的大小比率
- SurvivorRatio = 8 //對應jvm啟動參數-XX:SurvivorRatio=設置年輕代中Eden區與Survivor區的大小比值
- PermSize = 21757952 (20.75MB) //對應jvm啟動參數-XX:PermSize=<value>:設置JVM堆的‘永生代’的初始大小
- MaxPermSize = 85983232 (82.0MB)//對應jvm啟動參數-XX:MaxPermSize=<value>:設置JVM堆的‘永生代’的***大小
- G1HeapRegionSize = 0 (0.0MB)
- Heap Usage://堆內存使用情況
- PS Young Generation
- Eden Space://Eden區內存分布
- capacity = 33030144 (31.5MB)//Eden區總容量
- used = 1524040 (1.4534378051757812MB) //Eden區已使用
- free = 31506104 (30.04656219482422MB) //Eden區剩余容量
- 4.614088270399305% used //Eden區使用比率
- From Space: //其中一個Survivor區的內存分布
- capacity = 5242880 (5.0MB)
- used = 0 (0.0MB)
- free = 5242880 (5.0MB)
- 0.0% used
- To Space: //另一個Survivor區的內存分布
- capacity = 5242880 (5.0MB)
- used = 0 (0.0MB)
- free = 5242880 (5.0MB)
- 0.0% used
- PS Old Generation //當前的Old區內存分布
- capacity = 86507520 (82.5MB)
- used = 0 (0.0MB)
- free = 86507520 (82.5MB)
- 0.0% used
- PS Perm Generation//當前的 “永生代” 內存分布
- capacity = 22020096 (21.0MB)
- used = 2496528 (2.3808746337890625MB)
- free = 19523568 (18.619125366210938MB)
- 11.337498256138392% used
- 670 interned Strings occupying 43720 bytes.
可以很清楚的看到Java堆中各個區域目前的情況。
-histo
打印堆的對象統計,包括對象數、內存大小等等 (因為在dump:live前會進行full gc,如果帶上live則只統計活對象,因此不加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
- [ 數組,如[I表示int[]
- [L+類名 其他對象
-permstat
打印Java堆內存的***保存區域的類加載器的智能統計信息。對于每個類加載器而言,它的名稱、活躍度、地址、父類加載器、它所加載的類的數量和大小都會被打印。此外,包含的字符串數量和大小也會被打印。
- $ 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內置了一個微型的HTTP/HTML服務器,生成dump的分析結果后,可以在瀏覽器中查看。在此要注意,一般不會直接在服務器上進行分析,因為jhat是一個耗時并且耗費硬件資源的過程,一般把服務器生成的dump文件復制到本地或其他機器上進行分析。
命令格式
- jhat [dumpfile]
參數
- -stack false|true 關閉對象分配調用棧跟蹤(tracking object allocation call stack)。 如果分配位置信息在堆轉儲中不可用. 則必須將此標志設置為 false. 默認值為 true.>
- -refs false|true 關閉對象引用跟蹤(tracking of references to objects)。 默認值為 true. 默認情況下, 返回的指針是指向其他特定對象的對象,如反向鏈接或輸入引用(referrers or incoming references), 會統計/計算堆中的所有對象。>
- -port port-number 設置 jhat HTTP server 的端口號. 默認值 7000.>
- -exclude exclude-file 指定對象查詢時需要排除的數據成員列表文件(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 指定一個基準堆轉儲(baseline heap dump)。 在兩個 heap dumps 中有相同 object ID 的對象會被標記為不是新的(marked as not being new). 其他對象被標記為新的(new). 在比較兩個不同的堆轉儲時很有用.>
- -debug int 設置 debug 級別. 0 表示不輸出調試信息。 值越大則表示輸出更詳細的 debug 信息.>
- -version 啟動后只顯示版本信息就退出>
- -J< flag > 因為 jhat 命令實際上會啟動一個JVM來執行, 通過 -J 可以在啟動JVM時傳入一些啟動參數. 例如, -J-Xmx512m 則指定運行 jhat 的Java虛擬機使用的***堆內存為 512 MB. 如果需要使用多個JVM啟動參數,則傳入多個 -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內存去啟動HTTP服務器,運行完之后就可在瀏覽器打開Http://localhost:7000進行快照分析 堆快照分析主要在***面的Heap Histogram里,里面根據class列出了dump的時候所有存活對象。
分析同樣一個dump快照,MAT需要的額外內存比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 以樹狀圖形式展示堆情況。如下圖:

具體排查時需要結合代碼,觀察是否大量應該被回收的對象在一直被引用或者是否有占用內存特別大的對象無法被回收。
一般情況,會down到客戶端用工具來分析
jstack
jstack用于生成java虛擬機當前時刻的線程快照。線程快照是當前java虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間等待等。 線程出現停頓的時候通過jstack來查看各個線程的調用堆棧,就可以知道沒有響應的線程到底在后臺做什么事情,或者等待什么資源。 如果java程序崩潰生成core文件,jstack工具可以用來獲得core文件的java stack和native stack的信息,從而可以輕松地知道java程序是如何崩潰和在程序何處發生問題。另外,jstack工具還可以附屬到正在運行的java程序中,看到當時運行的java程序的java stack和native stack的信息, 如果現在運行的java程序呈現hung的狀態,jstack是非常有用的。
命令格式
- jstack [option] LVMID
option參數
- -F : 當正常輸出請求不被響應時,強制輸出線程堆棧
- -l : 除堆棧外,顯示關于鎖的附加信息
- -m : 如果調用到本地方法的話,可以顯示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
- .....
分析
這里有一篇文章解釋的很好 分析打印出的文件內容
jinfo
jinfo(JVM Configuration info)這個命令作用是實時查看和調整虛擬機運行參數。 之前的jps -v口令只能查看到顯示指定的參數,如果想要查看未被顯示指定的參數的值就要使用jinfo口令
命令格式
- jinfo [option] [args] LVMID
option參數
- -flag : 輸出指定args參數的值
- -flags : 不需要args參數,輸出所有JVM參數的值
- -sysprops : 輸出系統屬性,等同于System.getProperties()
示例
- $ jinfo -flag 11494
- -XX:CMSInitiatingOccupancyFraction=80
【本文為51CTO專欄作者“純潔的微笑”的原創稿件,轉載請通過微信公眾號聯系作者獲取授權】