JDK 為我們提供的工具
這些工具在 windows 上,就是這些 exe,其他的平臺(tái)不同。

在 linux 中,一般自帶了 OpenJdk,一般情況下 JPS 等命令不能用,要么選擇去安裝 JPS 等插件,要么把 OpenJdk 卸載,去重新安裝 Oracle 的 JDK,我推薦后者。
命令行工具
jps
列出當(dāng)前機(jī)器上正在運(yùn)行的虛擬機(jī)進(jìn)程,JPS 從操作系統(tǒng)的臨時(shí)目錄上去找(所以有一些信息可能顯示不全)。

-q :僅僅顯示進(jìn)程。
-m:輸出主函數(shù)傳入的參數(shù). 下的 hello 就是在執(zhí)行程序時(shí)從命令行輸入的參數(shù)。
-l: 輸出應(yīng)用程序主類完整 package 名稱或 jar 完整名稱。
-v: 列出 jvm 參數(shù), -Xms20m -Xmx50m 是啟動(dòng)程序指定的 jvm 參數(shù)。
jstat
是用于監(jiān)視虛擬機(jī)各種運(yùn)行狀態(tài)信息的命令行工具。它可以顯示本地或者遠(yuǎn)程虛擬機(jī)進(jìn)程中的類裝載、內(nèi)存、垃圾收集、JIT 編譯等運(yùn)行數(shù)據(jù),在沒(méi)有 GUI圖形界面,只提供了純文本控制臺(tái)環(huán)境的服務(wù)器上,它將是運(yùn)行期定位虛擬機(jī)性能問(wèn)題的首選工具。
常用參數(shù):
-class (類加載器)?
-compiler (JIT)?
-gc (GC 堆狀態(tài))
-gccapacity (各區(qū)大小)
?-gccause (最近一次 GC 統(tǒng)計(jì)和原因)?
-gcnew (新區(qū)統(tǒng)計(jì))?
-gcnewcapacity (新區(qū)大小)?
-gcold (老區(qū)統(tǒng)計(jì))?
-gcoldcapacity (老區(qū)大小)?
-gcpermcapacity (永久區(qū)大小)?
-gcutil (GC 統(tǒng)計(jì)匯總)?
-printcompilation (HotSpot 編譯統(tǒng)計(jì))
比如說(shuō):我們要統(tǒng)計(jì) GC,就是垃圾回收,那么只需要使用這樣的命令。
jstat-gc 13616 (這個(gè) 13616 是 JVM 的進(jìn)程,通過(guò) JPS 命令得到),這樣統(tǒng)計(jì)出來(lái)是的實(shí)時(shí)值。
所以很多情況下,我們?yōu)榱丝醋兓档模梢赃@么玩。

假設(shè)需要每 250 毫秒查詢一次進(jìn)程 13616 垃圾收集狀況,一共查詢 10 次,那命令應(yīng)當(dāng)是:jstat-gc 13616 250 10。
S0C:第一個(gè)幸存區(qū)(From 區(qū))的大小
S1C:第二個(gè)幸存區(qū)(To 區(qū))的大小
S0U:第一個(gè)幸存區(qū)的使用大小
S1U:第二個(gè)幸存區(qū)的使用大小
EC:伊甸園(Eden)區(qū)的大小
EU:伊甸園(Eden)區(qū)的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法區(qū)大小
MU:方法區(qū)使用大小
CCSC:壓縮類空間大小
CCSU:壓縮類空間使用大小
YGC:年輕代垃圾回收次數(shù)
YGCT:年輕代垃圾回收消耗時(shí)間
FGC:老年代垃圾回收次數(shù)
FGCT:老年代垃圾回收消耗時(shí)間
GCT:垃圾回收消耗總時(shí)間
jinfo
查看和修改虛擬機(jī)的參數(shù)
jinfo –sysprops 可以查看由 System.getProperties()取得的參數(shù)
jinfo –flag 未被顯式指定的參數(shù)的系統(tǒng)默認(rèn)值
jinfo –flags(注意 s)顯示虛擬機(jī)的參數(shù)
VM 參數(shù)分類
JVM 的命令行參數(shù)參考:https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html

1、標(biāo)準(zhǔn): -開(kāi)頭,所有的 HotSpot 都支持
保證 Java 虛擬機(jī)(JVM)的所有實(shí)現(xiàn)都支持標(biāo)準(zhǔn)選項(xiàng)。它們用于執(zhí)行常見(jiàn)操作,例如檢查 JRE 版本,設(shè)置類路徑,啟用詳細(xì)輸出等。

2、非標(biāo)準(zhǔn):-X 開(kāi)頭,特定版本 HotSpot 支持特定命令非標(biāo)準(zhǔn)選項(xiàng)是特定于 Java HotSpot 虛擬機(jī)的通用選項(xiàng),因此不能保證所有 JVM 實(shí)現(xiàn)都支持它們,并且它們可能會(huì)發(fā)生變化。
這些選項(xiàng)以開(kāi)頭-X。
-Xms30m -Xmx30m -Xss1m

3、高級(jí)選項(xiàng):以開(kāi)頭-XX:
這些是開(kāi)發(fā)人員選項(xiàng),用于調(diào)整 Java HotSpot 虛擬機(jī)操作的特定區(qū)域,這些區(qū)域通常具有特定的系統(tǒng)要求,并且可能需要對(duì)系統(tǒng)配置參數(shù)的特權(quán)訪問(wèn)。也不能保證所有 JVM 實(shí)現(xiàn)都支持它們,并且它們可能會(huì)發(fā)生變化。

在 windows 上可以通過(guò)以下 java -XX:+PrintFlagsFinal –version 查詢所有-XX 的

注意:manageable 的參數(shù),代表可以運(yùn)行時(shí)修改。
演示例子如下:首先我們得知 PrintGC 這個(gè) XX 參數(shù)是可以運(yùn)行時(shí)修改的。
jinfo –flag -[參數(shù)] pid 可以修改參數(shù);
Thread.getAllStackTraces();
案例:StopWorld 類。
1.程序運(yùn)行時(shí)沒(méi)有打印 GC:

2.通過(guò) jinfo 查看參數(shù),打印 GC 詳情:

總結(jié):通過(guò) jinfo 命令,我可以在生產(chǎn)上臨時(shí)打開(kāi)一下 GC 日志或者進(jìn)行一些數(shù)據(jù)的配置。(不需要重啟應(yīng)用條件下),也是我們?nèi)ヅ挪閱?wèn)題的一個(gè)關(guān)鍵命令。
jmap
用于生成堆轉(zhuǎn)儲(chǔ)快照(一般稱為 heapdump 或 dump 文件)。jmap 的作用并不僅僅是為了獲取 dump 文件,它還可以查詢 finalize 執(zhí)行隊(duì)列、Java 堆和永久代的詳細(xì)信息,如空間使用率、當(dāng)前用的是哪種收集器等。
和 jinfo 命令一樣,jmap 有不少功能在 Windows 平臺(tái)下都是受限的,除了生成 dump 文件的-dump 選項(xiàng)和用于查看每個(gè)類的實(shí)例、空間占用統(tǒng)計(jì)的-histo 選項(xiàng)在所有操作系統(tǒng)都提供之外,其余選項(xiàng)都只能在 Linux/Solaris 下使用。
-heap 打印 heap 的概要信息jmap –heap

Heap Configuration: ##堆配置情況,也就是 JVM 參數(shù)配置的結(jié)果[平常說(shuō)的 tomcat 配置 JVM 參數(shù),就是在配置這些]
MinHeapFreeRatio = 40 ##最小堆使用比例
MaxHeapFreeRatio = 70 ##最大堆可用比例
MaxHeapSize = 2147483648 (2048.0MB) ##最大堆空間大小
NewSize = 268435456 (256.0MB) ##新生代分配大小
MaxNewSize = 268435456 (256.0MB) ##最大可新生代分配大小
OldSize = 5439488 (5.1875MB) ##老年代大小
NewRatio = 2 ##新生代比例
SurvivorRatio = 8 ##新生代與 suvivor 的比例
PermSize = 134217728 (128.0MB) ##perm 區(qū) 永久代大小
MaxPermSize = 134217728 (128.0MB) ##最大可分配 perm 區(qū) 也就是永久代大小
Heap Usage: ##堆使用情況【堆內(nèi)存實(shí)際的使用情況】
New Generation (Eden + 1 Survivor Space): ##新生代(伊甸區(qū) Eden 區(qū) + 幸存區(qū) survior(1+2)空間)
capacity = 241631232 (230.4375MB) ##伊甸區(qū)容量
used = 77776272 (74.17323303222656MB) ##已經(jīng)使用大小
free = 163854960 (156.26426696777344MB) ##剩余容量
32.188004570534986% used ##使用比例
Eden Space: ##伊甸區(qū)
capacity = 214827008 (204.875MB) ##伊甸區(qū)容量
used = 74442288 (70.99369812011719MB) ##伊甸區(qū)使用
free = 140384720 (133.8813018798828MB) ##伊甸區(qū)當(dāng)前剩余容量
34.65220164496263% used ##伊甸區(qū)使用情況
From Space: ##survior1 區(qū)
capacity = 26804224 (25.5625MB) ##survior1 區(qū)容量
used = 3333984 (3.179534912109375MB) ##surviror1 區(qū)已使用情況
free = 23470240 (22.382965087890625MB) ##surviror1 區(qū)剩余容量
12.43827838477995% used ##survior1 區(qū)使用比例
To Space: ##survior2 區(qū)
capacity = 26804224 (25.5625MB) ##survior2 區(qū)容量
used = 0 (0.0MB) ##survior2 區(qū)已使用情況
free = 26804224 (25.5625MB) ##survior2 區(qū)剩余容量
0.0% used ## survior2 區(qū)使用比例
PS Old Generation: ##老年代使用情況
capacity = 1879048192 (1792.0MB) ##老年代容量
used = 30847928 (29.41887664794922MB) ##老年代已使用容量
free = 1848200264 (1762.5811233520508MB) ##老年代剩余容量
1.6416783843721663% used ##老年代使用比例
-histo 打印每個(gè) class 的實(shí)例數(shù)目,內(nèi)存占用,類全名信息.
jmap –histojmap?
–histo:live 如果 live 子參數(shù)加上后,只統(tǒng)計(jì)活的對(duì)象數(shù)量。

但是這樣顯示太多了,一般在 linux 上會(huì)這么操作:jmap –histo 1196 | head -20(這樣只會(huì)顯示排名前 20 的數(shù)據(jù))。
不太重要的參數(shù):
-finalizerinfo 打印正等候回收的對(duì)象的信息,還有 jmap –clstats 這個(gè)命令最好也不要去使用

-dump 生成的堆轉(zhuǎn)儲(chǔ)快照(比較重要)
jmap -dump:live,format=b,file=heap.bin<pid>
Sun JDK 提供 jhat(JVM Heap Analysis Tool)命令與 jmap 搭配使用,來(lái)分析 jmap 生成的堆轉(zhuǎn)儲(chǔ)快照。

jhat
jhat dump 文件名
后屏幕顯示“Server is ready.”的提示后,用戶在瀏覽器中鍵入 http://localhost:7000/就可以訪問(wèn)詳情。

使用 jhat 可以在服務(wù)器上生成堆轉(zhuǎn)儲(chǔ)文件分析(一般不推薦,畢竟占用服務(wù)器的資源,比如一個(gè)文件就有 1 個(gè) G 的話就需要大約吃一個(gè) 1G 的內(nèi)存資源)。
jstack
(Stack Trace for Java)命令用于生成虛擬機(jī)當(dāng)前時(shí)刻的線程快照。線程快照就是當(dāng)前虛擬機(jī)內(nèi)每一條線程正在執(zhí)行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現(xiàn)長(zhǎng)時(shí)間停頓的原因,如線程間死鎖、死循環(huán)、請(qǐng)求外部資源導(dǎo)致的長(zhǎng)時(shí)間等待等都是導(dǎo)致線程長(zhǎng)時(shí)間停頓的常見(jiàn)原因。
在代碼中可以用 java.lang.Thread 類的 getAllStackTraces()方法用于獲取虛擬機(jī)中所有線程的 StackTraceElement 對(duì)象。使用這個(gè)方法可以通過(guò)簡(jiǎn)單的幾行代碼就完成 jstack 的大部分功能,在實(shí)際項(xiàng)目中不妨調(diào)用這個(gè)方法做個(gè)管理員頁(yè)面,可以隨時(shí)使用瀏覽器來(lái)查看線程堆棧。(并發(fā)編程中有具體的案例)
一般來(lái)說(shuō) jstack 主要是用來(lái)排查是否有死鎖的情況,這塊內(nèi)容在并發(fā)編程中有詳細(xì)的講解。


命令工具總結(jié)
生產(chǎn)服務(wù)器推薦開(kāi)啟
-XX:-HeapDumpOnOutOfMemoryError 默認(rèn)關(guān)閉,建議開(kāi)啟,在 java.lang.OutOfMemoryError 異常出現(xiàn)時(shí),輸出一個(gè) dump 文件,記錄當(dāng)時(shí)的堆內(nèi)存快照。
-XX:HeapDumpPath=./java_pid.hprof 用來(lái)設(shè)置堆內(nèi)存快照的存儲(chǔ)文件路徑,默認(rèn)是 java 進(jìn)程啟動(dòng)位置。
調(diào)優(yōu)之前開(kāi)啟、調(diào)優(yōu)之后關(guān)閉
-XX:+PrintGC
調(diào)試跟蹤之 打印簡(jiǎn)單的 GC 信息參數(shù): -XX:+PrintGCDetails, +XX:+PrintGCTimeStamps
打印詳細(xì)的 GC 信息。
-Xlogger:logpath
設(shè)置 gc 的日志路,如: -Xlogger:log/gc.log, 將 gc.log 的路徑設(shè)置到當(dāng)前目錄的 log 目錄下。
?應(yīng)用場(chǎng)景: 將 gc 的日志獨(dú)立寫入日志文件,將 GC 日志與系統(tǒng)業(yè)務(wù)日志進(jìn)行了分離,方便開(kāi)發(fā)人員進(jìn)行追蹤分析。
考慮使用
-XX:+PrintHeapAtGC, 打印推信息。
參數(shù)設(shè)置: -XX:+PrintHeapAtGC
應(yīng)用場(chǎng)景: 獲取 Heap 在每次垃圾回收前后的使用狀況。
-XX:+TraceClassLoading
參數(shù)方法: -XX:+TraceClassLoading
應(yīng)用場(chǎng)景: 在系統(tǒng)控制臺(tái)信息中看到 class 加載的過(guò)程和具體的 class 信息,可用以分析類的加載順序以及是否可進(jìn)行精簡(jiǎn)操作。
-XX:+DisableExplicitGC 禁止在運(yùn)行期顯式地調(diào)用 System.gc()
可視化工具
JMX(Java Management Extensions,即 Java 管理擴(kuò)展)是一個(gè)為應(yīng)用程序、設(shè)備、系統(tǒng)等植入管理功能的框架。JMX 可以跨越一系列異構(gòu)操作系統(tǒng)平臺(tái)、系統(tǒng)體系結(jié)構(gòu)和網(wǎng)絡(luò)傳輸協(xié)議,靈活的開(kāi)發(fā)無(wú)縫集成的系統(tǒng)、網(wǎng)絡(luò)和服務(wù)管理應(yīng)用。
管理遠(yuǎn)程進(jìn)程需要在遠(yuǎn)程程序的啟動(dòng)參數(shù)中增加:
-Djava.rmi.server.hostname=…..?
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8888
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
Jconsole




visualvm
插件中心地址:https://visualvm.github.io
但是注意版本問(wèn)題,不同的 JDK 所帶的 visualvm 是不一樣的,下載插件時(shí)需要下對(duì)應(yīng)的版本。
一般來(lái)說(shuō),這個(gè)工具是本機(jī)調(diào)試用,一般生產(chǎn)上來(lái)說(shuō),你一般是用不了的(除非啟用遠(yuǎn)程連接)。






Arthas
官方文檔參考 https://alibaba.github.io/arthas/
Arthas 是 Alibaba 開(kāi)源的 Java 診斷工具,深受開(kāi)發(fā)者喜愛(ài)。
Arthas 支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同時(shí)提供豐富的 Tab 自動(dòng)補(bǔ)全功能,進(jìn)一步方便進(jìn)行問(wèn)題的定位和診斷。
下載和安裝
不需要安裝,就是一個(gè) jar 包。
curl -O https://alibaba.github.io/arthas/arthas-boot.jar
java -jar arthas-boot.jar
啟動(dòng) arthas 的 jar 包是 arthas-boot.jar
快速入門
1、直接 java -jar arthas-boot.jar。選擇 attach 的進(jìn)程綁定

2.通過(guò) jps 命令快速查找 java 進(jìn)程,再次直接綁定 java -jar arthas-boot.jar pid ,啟動(dòng) arthas 工具 attach 到目標(biāo)進(jìn)程。


進(jìn)入 arthas 后命令行前面出現(xiàn)標(biāo)識(shí)。

常用命令
Dashboard
注意在 arthas 中,有 tab 鍵填充功能,所以比較好用。但是這個(gè)界面是實(shí)時(shí)刷新的,一般 5s 刷新一次,使用 q 鍵退出刷新(沒(méi)有退出 arthasq)。


Thread
這個(gè)命令和 jstack 很相似,但是功能更加強(qiáng)大,主要是查看當(dāng)前 JVM 的線程堆棧信息,同時(shí)可以結(jié)合使用 thread –b 來(lái)進(jìn)行死鎖的排查死鎖。
參數(shù)解釋:
-n 指定最忙的前 n 個(gè)線程并打印堆棧
-b 找出阻塞當(dāng)前線程的線程
-i 指定 cpu 占比統(tǒng)計(jì)的采樣間隔,單位為毫秒
實(shí)戰(zhàn)演示
thread –h 顯示幫助

thread –b 找出阻塞當(dāng)前線程的線程

如果有死鎖,會(huì)有紅色的字提醒著,這個(gè)阻塞的線程已經(jīng)被另外一個(gè)線程阻塞:

thread -i 1000 -n 3 每過(guò) 1000 毫秒進(jìn)行采樣,顯示最占 CPU 時(shí)間的前 3 個(gè)線程

thread --state WAITING 查看處于等待狀態(tài)的線程

JVM
jvm:

Jad
反編譯指定已加載類的源碼

trace
使用 trace 命令可以跟蹤統(tǒng)計(jì)方法耗時(shí)。
繼續(xù)跟蹤耗時(shí)高的方法,然后再次訪問(wèn)。
比如使用一個(gè) Springboot 項(xiàng)目(當(dāng)然,不想 Springboot 的話,你也可以直接在 UserController 里 main 方法啟動(dòng))控制層 getUser 方法調(diào)用了 userService.get(uid);,這個(gè)方法中分別進(jìn)行 check、service、redis、mysql 等操作操作。就可以根據(jù)這個(gè)命令跟蹤出來(lái)哪里的耗時(shí)最長(zhǎng)。

monitor
每 5 秒統(tǒng)計(jì)一次 cn.enjoyedu.demo.controller.DemoController 類的 test 方法執(zhí)行情況:

watch
觀察方法的入?yún)⒊鰠⑿畔?/p>
# 查看入?yún)⒑统鰠?/p>
$ watch cn.enjoyedu.demo.controller.DemoController test '{params[0],returnObj}'


Arthas命令匯總

