基於JDK命令行工具的監控
一、JVM的參數類型
JVM的參數類型主要分成三類
- 標準參數
- X參數
- XX參數
標準參數,在JVM的各個版本中基本不變的(儘可能保持兼容),是相對比較穩定的參數。
比方說,大家在第一次安裝Java後,都會敲的命令行
java -version
裏面會顯示JVM的一些基本信息,比如版本號,和編譯方式,這裏的minxed mode表示混合編譯,還有Server VM 表示JVM的運行模式是Server模式。
除了-version還有-help
java -help
它可以列舉一些Java的標準參數,並標有解釋。
下面還有很多,這裏僅僅列舉一部分。
這裏說兩個參數: -server 和 -client
JVM有兩種運行模式Server與Client。兩種模式的區別在於,Client模式啓動速度較快,Server模式啓動較慢;但是啓動進入穩定期長期運行之後Server模式的程序運行速度比Client要快很多。這是因爲Server模式啓動的JVM採用的是重量級的虛擬機,對程序採用了更多的優化;而Client模式啓動的JVM採用的是輕量級的虛擬機。
server:默認爲堆提供了一個更大的空間和並行的垃圾收集器 並且在運行時可以更大程度的優化代碼
client:客戶端虛擬機有較小的默認堆內存 可以縮短JVM啓動的時間和佔用更少的內存 客戶端的JVM只有在32位操作系統中才有
值得我們關注的是:
- 從JDK5開始 當應用啓動時會檢測當前的運行環境是否是服務器 如果是服務器就使用Server JVM
- 在JDK6中 Server JVM要求至少雙核CPU和2GB物理內存
- 在32位操作系統上 JDK可以運行Server JVM 但是JRE只能運行Client JVM
這些瞭解即可,一般不會變動。
X參數
X參數和XX參數都是非標準化參數,在各個JVM版本中可能會變化。
不過X參數相對於XX參數,使用頻率低很多,下面看幾個常見的。
設置JVM的編譯方式:
- -Xint:解釋執行
- -Xcomp:第一次使用就編譯成本地代碼
- -Xmixed:混合模式,JVM自己來決定是否如何編譯本地代碼
使用演示:
這裏再還原成混合模式:
默認的混合模式 是最好的。
XX參數
XX參數主要分爲兩大類:
Boolean類型
格式:
-XX:[+-] <name> 表示啓用或者禁用了name參數 +表示啓用
比如:
- -XX:+UseConcMarkSweepGC表示啓用了CMS垃圾回收器
- -XX:+UseG1GC 表示啓用了G1垃圾回收器
非Boolean(Key-Value類型)
格式:
-XX:<name>=<value> 表示name參數的值是value
比如:
- -XX:MaxGCPauseMills=500表示GC最大的停頓時間是500
- -XX:GCTimeRatio=19
GCTimeRatio參數的值應當是一個大於0且小於100的整數,也就是垃圾收集時間佔總時間的比率,相當於是吞吐量的倒數。如果把此參數設置爲19,那允許的最大GC時間就佔總時間的5%(即1/(1+19)),默認值爲99,就是允許最大1%(即1/(1+99))的垃圾收集時間。
-XX參數常見的參數還有一種縮寫模式(雖然是-X開頭,但是其實是-XX參數)
比如:
- -Xms 等價於-XX:InitialHeapSize 表示初始化的堆大小
- -Xmx 等價於-XX:MaxHeapSize 表示最大的堆的大小
- -Xss 等價於 -XX:ThreadStackSize 線程堆棧的大小
二、JDK的命令行監控工具
查看JVM運行時參數的值
1、-XX:+PrintFlagsFina
1 ) -XX:+PrintFlagsFinal 打印出所有XX參數和值
java -XX: +PrintFlagsFinal
=表示默認值
:= 表示被用戶或者JVM修改後的值
2、-XX:+PrintFlagsInitial
java -XX:+PrintFlagsInitial
打印的是XX參數的初始化值
這裏列舉一小部分:
3、-XX:+PrintCommandLineFlags
這個參數讓JVM打印出那些已經被用戶或者JVM設置過的詳細的XX參數的名稱和值。
它列舉出 -XX:+PrintFlagsFinal的結果中第三列有”:=”的參數。
以這種方式,我們可以用-XX:+PrintCommandLineFlags作爲快捷方式來查看修改過的參數
正常使用
java -XX:+PrintCommandLineFlags
可以查看到垃圾回收器的信息
別人測試的都能看到垃圾回收器的信息,我的JVM並不能看到這個信息,暫時沒有找到原因,這裏記錄一下。下面兩張圖引自傳送門
4、-XX:+UnlockDiagnosticVMOptions解鎖診斷參數
5、-XX:+UnlockExperimentalVMOptions解鎖實驗參數
這兩條後面會補上。
(1)jps
在介紹這幾款工具之前,有必要說明:JDK內置命令行工具的監控的詳細信息都可以在這裏找到 傳送門。
jps是虛擬機進程狀況工具
下面到了JDK的命令行監控工具的使用,首先是jps,使用非常簡單。
Java的jps 是專門查看Java進程的
加一個 -l參數,可以看到完整的名稱
jps工具主要選項
(2)jinfo
jinfo 是 Java配置信息工具
jinfo(Configuration Info for Java)的作用是實時查看和調整虛擬機各項參數。
使用jps命令的-v參數可以查看虛擬機啓動時顯式指定的參數列表,但如果想知道未被顯式指定的參數的系統默認值,除了去找資料外,就只能使用jinfo的-flag選項進行查詢了(如果只限於JDK 6或以上版本的話,使用java-XX:+PrintFlagsFinal查看參數默認值也是一個很好的選擇)。
jinfo還可以使用-sysprops選項把虛擬機進程的System.getProperties()的內容打印出來。這個命令在JDK5時期已經隨着Linux版的JDK發佈,當時只提供了信息查詢的功能,JDK 6之後,jinfo在Windows和Linux平臺都有提供,並且加入了在運行期修改部分參數值的能力(可以使用-flag[+|-]name或者-flag name=value在運行期修改一部分運行期可寫的虛擬機參數值)。在JDK 6中,jinfo對於Windows平臺功能仍然有較大限制,只提供了最基本的-flag選項。
可以使用 jinfo -flag <參數名稱> 命令行工具去查看當前java進程的JVM參數。
比如:
jinfo -flag MaxHeapSize pid
查看線程堆棧的大小 這裏顯示是1024KB。
查看是否使用了啓用相關的配置。比如壓縮指針。
+ 表示有
- 表示沒有
在堆中,32位的對象引用(指針)佔4個字節,而64位的對象引用佔8個字節。也就是說,64位的對象引用大小是32位的2倍。64位JVM在支持更大堆的同時,由於對象引用變大卻帶來了性能問題。爲了能夠保持32位的性能,oop須保留32位。方法是壓縮指針
(3)jstat
jstat是虛擬機統計信息監視工具
確切點來說,jstat(JVM Statistics Monitoring Tool)是用於監視虛擬機各種運行狀態信息的命令行工具。它可以顯示本地或者遠程[插圖]虛擬機進程中的類加載、內存、垃圾收集、即時編譯等運行時數據,在沒有GUI圖形界面、只提供了純文本控制檯環境的服務器上,它將是運行期定位虛擬機性能問題的常用工具。
比如類裝載信息、垃圾收集信息、以及JIT編譯信息等
命令格式如下:
option主要有如下內容:
-class 查看類加載信息
-compiler 查看編譯信息
-gc 查看垃圾回收信息
更多的選項,可以從-help查看,或者想要了解更多, JDK內置命令行工具的監控的詳細信息都可以在這裏找到 傳送門
演示
java -class 進程號 間隔毫秒數 輸出次數
-class option
Class loader statistics.
Loaded: Number of classes loaded. -- 加載的類的個數
Bytes: Number of kBs loaded. -- 加載的類的大小
Unloaded: Number of classes unloaded -- 卸載的類的個數.
Bytes: Number of Kbytes unloaded. -- 卸載的類的大小
Time: Time spent performing class loading and unloading operations. -- 花在加載和卸載類的時間
垃圾收集的內容還是比較多的。
java -gc 進程號 間隔毫秒數 輸出次數
參數比較多哈,但是根據下面的註釋 很容易區分出JVM中的每一個分塊。
其中S0C、S0U。C表示總容量大小,U表示使用大小,這樣就好理解了。
- S0C、S1C、S0U、S1U:S0和S1的總量與使用量
- EC、EU:Eden區總量與使用量
- OC、OU:Old區總量與使用量
- MC、MU: Metaspace區總量與使用量
- CCSC、CCSU:壓縮類空間總量與使用量(壓縮指針的作用)
- YGC、YGCT:Young Go的次數與時間
- FGC、FGCT:FullGC的次數與時間
- GCT:總的GC時間
-gc option
Garbage-collected heap statistics.
S0C: Current survivor space 0 capacity (kB). --
S1C: Current survivor space 1 capacity (kB).
S0U: Survivor space 0 utilization (kB).
S1U: Survivor space 1 utilization (kB).
EC: Current eden space capacity (kB).
EU: Eden space utilization (kB).
OC: Current old space capacity (kB).
OU: Old space utilization (kB).
MC: Metaspace capacity (kB).
MU: Metacspace utilization (kB).
CCSC: Compressed class space capacity (kB).
CCSU: Compressed class space used (kB).
YGC: Number of young generation garbage collection events.
YGCT: Young generation garbage collection time.
FGC: Number of full GC events.
FGCT: Full garbage collection time.
GCT: Total garbage collection time.
除此之外 還有別的option。具體需要查文檔,演示的內容是比較常用的。
下面有必要說一下Java的內存結構:
JVM主要分成兩大塊,一塊是堆區、一塊是非堆區。
堆區又分成兩大塊,一塊是Young、一塊是Old。
如果young區是複製算法的話(現在的商用Java虛擬機大多都優先採用了這種收集算法去回收新生代)。
Young區會被分成兩大塊,一塊是S0+S1、另外一塊是Eden,對象優先在Eden分配。
發生垃圾收集時,將Eden和Survivor中仍然存活的對象一次性複製到另外一塊Survivor空間上,然後直接清理掉Eden和已用過的那塊Survivor空間。
HotSpot虛擬機默認Eden和Survivor的大小比例是8∶1,也即每次新生代中可用內存空間爲整個新生代容量的90%(Eden的80%加上一個Survivor的10%),只有一個Survivor空間,即10%的新生代是會被“浪費”的。
非堆區採用的是操作系統的本地內存,Java8中引入了元空間(方法區移入Metaspace),裏面有兩個重要的內容,一個是如果啓用了壓縮指針,就會存在CCS。CodeCahe中存放的是JIT的代碼信息(Java代碼轉換成的native代碼、JNI代碼)。
下面看一下JIT編譯的情況
java -compiler 進程號
java -printcompilation 進程號
-compiler option
Java HotSpot VM Just-in-Time compiler statistics.
Compiled: Number of compilation tasks performed. -- 完成多少編譯任務(將方法編譯成本地代碼)
Failed: Number of compilations tasks failed. -- 失敗的
Invalid: Number of compilation tasks that were invalidated. -- 錯誤的、無效的
Time: Time spent performing compilation tasks. -- 時間
FailedType: Compile type of the last failed compilation.
FailedMethod: Class name and method of the last failed compilation.
Compiled: Number of compilation tasks performed by the most recently compiled method.
Size: Number of bytes of byte code of the most recently compiled method.
Type: Compilation type of the most recently compiled method.
Method: Class name and method name identifying the most recently compiled method. Class name uses slash (/) instead of dot (.) as a name space separator. Method name is the method within the specified class. The format for these two fields is consistent with the HotSpot -XX:+PrintCompilation option.
其它選項的說明:
(4)jmap
jmap是Java內存映像工具
jmap(Memory Map for Java)命令可以用於生成堆轉儲快照(一般稱爲heapdump或dump文件),還可以查詢finalize執行隊列、Java堆和方法區的詳細信息,如空間使用率、當前用的是哪種收集器等。
和jinfo命令一樣,jmap有部分功能在Windows平臺下是受限的,除了生成堆轉儲快照的-dump選項和用於查看每個類的實例、空間佔用統計的-histo選項在所有操作系統中都可以使用之外,其餘選項都只能在Linux/Solaris中使用。
命令格式
jmap [option] vmid 進程號
option選項的合法值與具體含義如表示。
通過jmap可以定位內存溢出的問題。
我們看一下堆溢出
public class Main {
public static void main(String[] args) {
ArrayList list = new ArrayList();
while (true) {
list.add(new Main());
}
}
}
結果:java.lang.OutOfMemoryError
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.base/java.util.Arrays.copyOf(Arrays.java:3721)
at java.base/java.util.Arrays.copyOf(Arrays.java:3690)
at java.base/java.util.ArrayList.grow(ArrayList.java:235)
at java.base/java.util.ArrayList.grow(ArrayList.java:242)
at java.base/java.util.ArrayList.add(ArrayList.java:452)
at java.base/java.util.ArrayList.add(ArrayList.java:465)
at com.leetcodePractise.test.Main.main(Main.java:13)
Process finished with exit code 1
再看一下非堆溢出,非堆指的是非堆內存,主要包括元空間 , Jvm Stack(java虛擬機棧), Local Method Statck(本地方法棧)。
public class Main {
public static void main(String[] args) {
new Main().test();
}
public void test() {
test();
}
}
結果:java.lang.StackOverflowError
Exception in thread "main" java.lang.StackOverflowError
at com.leetcodePractise.test.Main.test(Main.java:10)
at com.leetcodePractise.test.Main.test(Main.java:10)
at com.leetcodePractise.test.Main.test(Main.java:10)
at com.leetcodePractise.test.Main.test(Main.java:10)
....
當發生內存溢出的時候,我們希望有內存映像文件的自動導出,來幫助我們去進行分析。這裏有兩種方式,一種是通過參數的設置
-XX:+HeapDumpOnOutOfMemoryError 意思是當發生內存溢出的時候將Heap dump出來
-XX:HeapDumpPath=./ 意思是導出的文件存放在哪個路徑下面
另外一種方式是使用jmap命令手動導出
dump文件直接打開是亂碼的,如果我們要分析dump文件,需要藉助工具MAT,可以參考這篇文章傳送門
下面演示一下其他的option
-heap 顯示每個區塊佔多大的內存。
其他的選項使用類型。
(5)jhat
jhat是虛擬機堆轉儲快照分析工具
JDK提供jhat(JVM Heap Analysis Tool)命令與jmap搭配使用,來分析jmap生成的堆轉儲快照。
jhat內置了一個微型的HTTP/Web服務器,生成堆轉儲快照的分析結果後,可以在瀏覽器中查看。
不過,在實際工作中,除非手上真的沒有別的工具可用,否則多數人是不會直接使用jhat命令來分析堆轉儲快照文件的,主要原因有兩個方面。
- 一般不會在部署應用程序的服務器上直接分析堆轉儲快照,即使可以這樣做,也會盡量將堆轉儲快照文件複製到其他機器上進行分析,因爲分析工作是一個耗時而且極爲耗費硬件資源的過程,既然都要在其他機器上進行,就沒有必要再受命令行工具的限制了。
- jhat的分析功能相對來說比較簡陋,有大量工具都能實現比jhat更強大專業的分析功能。比如上面介紹的MAT。
(6)jstack
jstack是Java堆棧跟蹤工具
jstack(Stack Trace for Java)命令用於生成虛擬機當前時刻的線程快照(一般稱爲threaddump或者javacore文件)。線程快照就是當前虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的目的通常是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間掛起等,都是導致線程長時間停頓的常見原因。線程出現停頓時通過jstack來查看各個線程的調用堆棧,就可以獲知沒有響應的線程到底在後臺做些什麼事情,或者等待着什麼資源。
jstack命令格式:
jstack [option] 進程號
選項
演示
這是JIT負責編譯的線程
線程狀態是一項非常重要的信息,記牢了。
想必大家都曾遇到這樣的問題:爲什麼有時中CPU佔用會過高,怎麼查看與解決?
關於CPU佔用過高的原因大致有這幾條:
1、大型程序:可能是編寫的程序有不合理的地方,也有可能是電腦配置低
2、病毒與木馬:病毒、木馬造成。比如大量的蠕蟲病毒在系統內部迅速複製,造成CPU佔用資源率據高不下。
3、磁盤碎片:經常對文檔進行復制和刪除,會使得硬盤中數據排列非常分散,使計算機在查找的時候速度變慢,從而佔用大量的CPU。
關於排查可以這樣做:
通過top命令查看是哪個進程導致CPU佔用率高 然後shitf+p 倒序排列
如果是Java進程引起的問題,我們就需要看一下Java進程中的詳細信息了。
在終端執行 top -H -p ,查詢pid進程下的所有線程消耗cpu的情況,然後shift+p倒序找到消耗cpu最高的線程id,如圖:
日誌文件中線程id是採用16進制的,所以要將查詢出來的10進制線程id轉換成16進制,數字轉換的方式有很多中,在這裏通過linux命令printf轉換,如下:
使用jstack下載堆棧信息日誌文件,sudo -u appadmin jstack 179258 >/usr/local/test2.txt,如下:
因爲當前用戶是root,而非179258進程所屬用戶appadmin,因此需要切換用戶。然後根據進程id將對應的日誌重定向到某個目錄下,當然也可以在線查找 sudo -u appadmin jstack <進程id> | grep -a <16進制線程id>.
生成的日誌文件位於linux上,可以通過sz下載到windows下進行查找,根據上文轉成16進制的線程id進行全局搜索,就能定位到具體的原因,如圖:
根據以上步驟能快速找到問題所在,但是這個日誌文件的分析是需要經驗積累的,只有當出現諸如線程死鎖的情況下,會出現很明顯的日誌,deadLock關鍵字樣,並且能提示出現問題的代碼所在行。其他問題如網絡帶寬,文件io等硬件資源跟不上的原因導致,是需要有一定的經驗積累才能發現的!
參考《深入理解Java虛擬機:JM高級特性與最佳實踐》