GC 調(diào)優(yōu)基礎(chǔ)知識(shí)之工具篇

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命令匯總

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容