jvm優化——垃圾回收
我們了解了jvm對內存分配以及垃圾回收是怎麼來處理的。理論是指導實踐的工具,有了理論指導,定位問題的時候,知識和經驗是關鍵基礎,數據可以為我們提供依據。
在常見的線上問題時候,我們多數會遇到以下問題:
內存泄露
某個進程突然cpu飆升
線程死鎖
響應變慢...等等其他問題。
如果遇到了以上這種問題,在線下可以有各種本地工具支持查看,但到線上了,就沒有這麼多的本地調試工具支持,我們該如何基於監控工具來進行定位問題?
我們一般會基於數據收集來定位,而數據的收集離不開監控工具的處理,比如:運行日誌、異常堆棧、GC日誌、線程快照、堆快照等。經常使用恰當的分析和監控工具可以加快我們的分析數據、定位解決問題的速度。以下我們將會詳細介紹。
一、jvm常見監控工具&指令
1、 jps:jvm進程狀況工具
jps [options] [hostid]
如果不指定hostid就默認為當前主機或伺服器。
命令行參數選項說明如下:
-q 不輸出類名、Jar名和傳入main方法的參數 - l 輸出main類或Jar的全限名 -m 輸出傳入main方法的參數 - v 輸出傳入JVM的參數
例如:
2、jstat: jvm統計信息監控工具
jstat 是用於見識虛擬機各種運行狀態信息的命令行工具。它可以顯示本地或者遠程虛擬機進程中的類裝載、內存、垃圾收集、jit編譯等運行數據,它是線上定位jvm性能的首選工具。
命令格式:
jstat [ generalOption | outputOptions vmid [interval[s|ms] [count]] ] generalOption - 單個的常用的命令行選項,如-help, -options, 或 -version。 outputOptions -一個或多個輸出選項,由單個的statOption選項組成,可以和-t, -h, and -J等選項配合使用。
參數選項:
Option
Displays
Ex
class
用於查看類加載情況的統計
jstat -class pid:顯示加載class的數量,及所占空間等信息。
compiler
查看HotSpot中即時編譯器編譯情況的統計
jstat -compiler pid:顯示VM實時編譯的數量等信息。
gc
查看JVM中堆的垃圾收集情況的統計
jstat -gc pid:可以顯示gc的信息,查看gc的次數,及時間。其中最後五項,分別是young gc的次數,young gc的時間,full gc的次數,full gc的時間,gc的總時間。
gccapacity
查看新生代、老生代及持久代的存儲容量情況
jstat -gccapacity:可以顯示,VM內存中三代(young,old,perm)對象的使用和占用大小
gccause
查看垃圾收集的統計情況(這個和-gcutil選項一樣),如果有發生垃圾收集,它還會顯示最後一次及當前正在發生垃圾收集的原因。
jstat -gccause:顯示gc原因
gcnew
查看新生代垃圾收集的情況
jstat -gcnew pid:new對象的信息
gcnewcapacity
用於查看新生代的存儲容量情況
jstat -gcnewcapacity pid:new對象的信息及其占用量
gcold
用於查看老生代及持久代發生GC的情況
jstat -gcold pid:old對象的信息
gcoldcapacity
用於查看老生代的容量
jstat -gcoldcapacity pid:old對象的信息及其占用量
gcpermcapacity
用於查看持久代的容量
jstat -gcpermcapacity pid: perm對象的信息及其占用量
gcutil
查看新生代、老生代及持代垃圾收集的情況
jstat -util pid:統計gc信息統計
printcompilation
HotSpot編譯方法的統計
jstat -printcompilation pid:當前VM執行的信息
例如:
查看gc 情況執行:jstat-gcutil 27777
3、jinfo: java配置信息
命令格式:
jinfo[option] pid
比如:獲取一些當前進程的jvm運行和啟動信息。
4、jmap: java 內存映射工具
jmap命令用於生產堆轉存快照。列印出某個java進程(使用pid)內存內的,所有『對象』的情況(如:產生那些對象,及其數量)。
命令格式:
jmap [ option ] pid jmap [ option ] executable core jmap [ option ] [server-id@]remote-hostname-or-IP
參數選項:
-dump:[live,]format=b,file=
例如:
使用jmap -heap pid查看進程堆內存使用情況,包括使用的GC算法、堆配置參數和各代中堆內存使用情況:
使用jmap -histo[:live] pid查看堆內存中的對象數目、大小統計直方圖。
5、jhat:jvm堆快照分析工具
jhat 命令與jamp搭配使用,用來分析map生產的堆快存儲快照。jhat內置了一個微型http/Html伺服器,可以在瀏覽器找那個查看。不過建議儘量不用,既然有dumpt文件,可以從生產環境拉取下來,然後通過本地可視化工具來分析,這樣既減輕了線上伺服器壓力,有可以分析的足夠詳盡(比如 MAT/jprofile/visualVm)等。
6、jstack:java堆棧跟蹤工具
jstack用於生成java虛擬機當前時刻的線程快照。線程快照是當前java虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的原因,如線程間死鎖、死循環、請求外部資源導致的長時間等待等。
命令格式:
jstack [ option ] pid jstack [ option ] executable core jstack [ option ] [server-id@]remote-hostname-or-IP
參數:
-F當』jstack [-l] pid』沒有相應的時候強制列印棧信息 -l長列表. 列印關於鎖的附加信息,例如屬於java.util.concurrent的ownable synchronizers列表. -m列印java和native c/c++框架的所有棧信息. -h | -help列印幫助信息 pid 需要被列印配置信息的java進程id,可以用jps查詢.
後續的查找耗費最高cpu例子會用到。
二、可視化工具
對jvm監控的常見可視化工具,除了jdk本身提供的Jconsole和visualVm以外,還有第三方提供的jprofilter,perfino,Yourkit,Perf4j,JProbe,MAT等。這些工具都極大的豐富了我們定位以及優化jvm方式。
這些工具的使用,網上有很多教程提供,這裡就不再過多介紹了。對於VisualVm來說,比較推薦使用,它除了對jvm的侵入性比較低以外,還是jdk團隊自己開發的,相信以後功能會更加豐富和完善。jprofilter對於第三方監控工具,提供的功能和可視化最為完善,目前多數ide都支持其插件,對於上線前的調試以及性能調優可以配合使用。
另外對於線上dump的heap信息,應該儘量拉去到線下用於可視化工具來分析,這樣分析更詳細。如果對於一些緊急的問題,必須需要通過線上監控,可以採用 VisualVm的遠程功能來進行,這需要使用tool.jar下的MAT功能。
三、應用
1、cpu飆升
在線上有時候某個時刻,可能會出現應用某個時刻突然cpu飆升的問題。對此我們應該熟悉一些指令,快速排查對應代碼。
1.找到最耗CPU的進程
指令:top
2.找到該進程下最耗費cpu的線程
指令:top -Hp pid
3.轉換進位
printf 「%xn」 15332 // 轉換16進位(轉換後為0x3be4)
4.過濾指定線程,列印堆棧信息
指令: jstack pid |grep 'threadPid' -C5 --color jstack 13525 |grep '0x3be4' -C5 --color // 列印進程堆棧 並通過線程id,過濾得到線程堆棧信息。
可以看到是一個上報程序,占用過多cpu了(以上例子只為示例,本身耗費cpu並不高)
2、線程死鎖
有時候部署場景會有線程死鎖的問題發生,但又不常見。此時我們採用jstack查看下一下。比如說我們現在已經有一個線程死鎖的程序,導致某些操作waiting中。
1.查找java進程id
指令:top 或者 jps
2.查看java進程的線程快照信息
指令:jstack -l pid
從輸出信息可以看到,有一個線程死鎖發生,並且指出了那行代碼出現的。如此可以快速排查問題。
3、OOM內存泄露
java堆內的OOM異常是實際應用中常見的內存溢出異常。一般我們都是先通過內存映射分析工具(比如MAT)對dump出來的堆轉存快照進行分析,確認內存中對象是否出現問題。
當然了出現OOM的原因有很多,並非是堆中申請資源不足一種情況。還有可能是申請太多資源沒有釋放,或者是頻繁頻繁申請,系統資源耗盡。針對這三種情況我需要一一排查。
OOM的三種情況:
1.申請資源(內存)過小,不夠用。
2.申請資源太多,沒有釋放。
3.申請資源過多,資源耗盡。比如:線程過多,線程內存過大等。
1.排查申請申請資源問題。
指令:jmap -heap 11869
查看新生代,老生代堆內存的分配大小以及使用情況,看是否本身份配過小。
從上述排查,發現程序申請的內存沒有問題。
2.排查gc
特別是fgc情況下,各個分代內存情況。
指令:jstat -gcutil 11938 1000 每秒輸出一次gc的分代內存分配情況,以及gc時間
3.查找最費內存的對象
指令: jmap -histo:live 11869 | more
上述輸出信息中,最大內存對象才161kb,屬於正常範圍。如果某個對象占用空間很大,比如超過了100Mb,應該著重分析,為何沒有釋放。
注意,上述指令:
jmap -histo:live 11869 | more 執行之後,會造成jvm強制執行一次fgc,在線上不推薦使用,可以採取dump內存快照,線下採用可視化工具進行分析,更加詳盡。 jmap -dump:format=b,file=/tmp/dump.dat 11869 或者採用線上運維工具,自動化處理,方便快速定位,遺失出錯時間。
4.確認資源是否耗盡
pstree 查看進程線程數量
netstat 查看網絡連接數量
或者採用:
ll /proc/${PID}/fd | wc -l // 打開的句柄數
ll /proc/${PID}/task | wc -l (效果等同pstree -p | wc -l) //打開的線程數
以上就是一些常見的jvm命令應用。
一種工具的應用並非是萬能鑰匙,包治百病,問題的解決往往是需要多種工具的結合才能更好的定位問題,無論使用何種分析工具,最重要的是熟悉每種工具的優勢和劣勢。這樣才能取長補短,配合使用。