本文目錄一覽:
- 1、jstack的使用
- 2、通過top和jstack確定哪些線程耗盡了CPU?這些線程在做什麼
- 3、CPU佔用過高,jstack導出後沒看出有什麼問題,求教
- 4、記一次線上機器CPU飆高的排查過程
- 5、JVM調優jstack怎麼找出最耗cpu的線程並定位代碼
jstack的使用
通過jstack,我們可以輕鬆得知jvm中各個線程的工作情況.
利用ps -aux 找出我們的java線程41,然後再用jstack -l 41,就可以查看jvm此刻運行的所有線程.
下面是截取的兩個jvm運行的普通線程,一個是守護線程,另外一個是用戶線程.
守護線程 守護線程是指給程序提供通用性支持的線程,他不屬於程序,gc就是一個很稱職的守護線程.守護線程是為用戶線程提供服務的,也就是說如果沒有用戶線程,守護線程就沒有存活下去的意義,在jstack中查出來的線程信息中,守護線程有個 daemon 的標誌
用戶線程 用戶線程通常是程序自己開啟的.jvm會隨著所有的用戶程序關閉而關閉
在下面的線程信息中 :
① HikariPool-1 connection closer 是線程的名字,在Java中可以通過Thread.currentThread().getName()來查看線程名字
② prio 應該是線程的優先順序
③ tid jvm中的線程id
③ nid tid映射的操作系統中的線程id,非常有用,不過這裡是用16進位的表示, 可以通過 printf “%x\n” 十進位數字 查找一個十進位數字的十六進位表示
④ 0x00007fa735a2a000 線程棧的起始地址
⑤ TIMED_WAITING 線程狀態
⑥ 0x00000006e941b160 資源名稱,等待某個資源被釋放,說明有其他線程鎖住了該資源,一般是 locked 0x00000006e941b160
線程狀態
假如java進程經常出現卡慢,cpu經常會爆滿,這時候我們考慮一下是否是我們某些線程太佔cpu,導致其他線程不能好好工作.可以通過以下步驟觀察
通過top和jstack確定哪些線程耗盡了CPU?這些線程在做什麼
1. 背景
有時,線上集群load會突然飆升,無法響應正常請求。
那麼引起load飆升的線程究竟在做什麼?哪些線程霸佔了CPU?可以通過top和jstack命令進行定位。
2. 定位步驟
1. 使用終端1進入目標機器,執行top命令,默認是進程視圖,其中PID是進程號,截圖如下:
在這裡,我們只能看到java進程佔用CPU達到115%,那麼究竟是那些線程非常耗CPU呢?
2. 由於我們要看到線程,在終端1,按下「H」鍵或者「shift+h」,top視圖會切換到線程視圖,其中PID是線程號,截圖如下:
可以發現紅框內的線程的CPU使用率非常高,佔用CPU時間達到1秒左右,顯然不正常,但是這些線程在做什麼?
3.
打開終端2,使用jstack命令輸出這一時刻的線程棧,保存到文件,命名為jstack.log。注意:輸出線程棧和保存top命令快照盡量同時進行。
4. 由於jstack.log文件記錄的線程ID是16進位,需要將top命令展示的線程號轉換為16進位,以15100為例,在linux下輸入命令:printf 0x%x 15100,得到15100的十六進位為0x3afc
5. 在jstack.log中搜索0x3afc關鍵字,可以清晰看到該線程在做刷新地址列表,如下圖:
3. 總結
以前碰到集群load飆升時,有時會束手無策,不知從何查起。以後再發生類似問題時,可以使用這個方法,看下究竟是那些線程在長時間佔用CPU,儘快定位問題和解決問題。
CPU佔用過高,jstack導出後沒看出有什麼問題,求教
同時按Ctrl ,Alt,del(小數點),打開任務管理器,看看是那個進程佔用高,在百度上查一下是什麼程序,沒用的果斷刪除
記一次線上機器CPU飆高的排查過程
公司如今把小貸機器都整理回收了,訪問量不大,基本都是用戶來查看賬戶進行還款操作。
現在情況是,我們把很多服務都放在了一台伺服器上,那天線上環境改了auth的salt,本地這邊是寫死的,自動上線已經關閉了通道,沒轍,手動替包手動上線,結果沒多久運維就喊了,表示cpu飆高到300%。
難得的機會,先用top找出cpu佔用最多進程
如果想細看進程信息可以使用ll proc
其實運行完這裡的時候,我比較吃驚的是,真正佔CPU的並不是部署的幾個服務,而是resin容器本身,飈到了99%,從這個角度來講,其實大部分性能問題都是垃圾回收的鍋。
然後利用ps查看到的進程pid找cpu最高線程
然後拿著線程tid在jstack找,結果在裡面找不到,然後上網查JAVA線程無法在jstack里找到的原因
以上大概意思是沒找到線程的跟蹤棧有三種情況,就是線程啟動前的預啟動,以及線程退出後的cleanup,第三種就是,利用JVM TI agent運行的線程,這個線程是用來跑本地code的。
******這裡面都提到了JVM TI技術,這個技術主要是用來提供虛擬機調用本地方法的介面,可以獲取jvm運行情況和提供本地方法的後門一樣的介面工具,一些調試和診斷工具都是基於JVM TI來實現的,很多JVM都有自己的TI實現。
當然,這個Stack Over Flow的答案針對的Java線程,我所知道的是,如果導致我們CPU飆高的並不是java線程,那麼jstack -F就打不出來。
而這個jstack -m模式,在官方文檔里是說,可以列印出所有Java線程和native frames的所有線程。其實到這裡是不是隱隱感覺到什麼了~~沒錯,最後真的是垃圾回收的鍋。
然後jdk8利用jstack -m找到了,發現裡面使用的方法是CMS垃圾回收器的方法
講真,平時只負責上下線,JVM配置只要不出問題很少留意,這個年代了,還在使用CMS嗎?嗯,jmap看了下,是CMS。而佔用大多CPU也是CMS的特性之一:最小化GC中斷,付出的代價便是高CPU負荷。
既然定位到了垃圾回收,那就接著排查垃圾回收吧,pid是18637
就去jstat -gc 18637 5000
jstat -gcold 18637
jstat -gcoldcapacity 18637
發現full gc非常頻繁(每五秒輸出,發現觸發fullGC13次)
(復盤這次的排查問題,發現圖片保存少之又少。。)
如此頻繁,順帶gccause一下看看最近一次回收的原因
結果長這樣
從這個結果來看最近一次是Metadata的GC,而觸發原因是因為達到了閾值。再細看當前元空間使用率已經接近98%。
到這裡不能等了,找運維拿來了jvm參數,有三個參數很亮眼
老年代的配置屬於默認配置,占整個堆內存的2/3,CMS回收要達到80%才會觸發,我們還沒有達到這個值。
那麼根據圖1中的顯示,metaspace我們已經達到了閾值。
這種情況產生也沒毛病,想想一台機器部署了5個web服務和5個微服務的場景吧,雖然沒什麼人訪問,但載入的class信息也足夠多了。
主要就是metadata的配置,而有關metadata的配置並沒有oracle的官方指導,官方指導上的意思是說metaspace的配置,主要是避免首次啟動時對載入的類信息做回收,取決於應用本身,夠用就行了,不要太頻繁觸發full gc就好
其實調大這metaspace的配置能夠大概解決問題,但是最終我選擇了切換為G1垃圾回收。
官方文檔是這樣寫的
首先戳我的點的主要原因是g1最終的目的是要取代cms垃圾回收器,它的回收範圍是整個堆,緊跟潮流總不會錯的。其次g1取代cms的情況官方也建議了以下幾點:
1. 一個是要管理的堆內存大於6g
2. 一個是服務已使用的堆內存超過了50%
3. 一個是對象分配率過高或對象從新生代晉陞到老年代速率過快(這裡我查了一下資料,意思是對象分配率過高的話其實會導致Minor GC頻繁,而這種情況會使對象更快地晉陞到老年代,而老年代如果過快地被填充,又會觸發FullGC。從設計角度來講,之所以分代回收,是為了應對對象存活時間而使用不同的回收策略,老年代可不是為了頻繁回收而產生的)
4. 垃圾回收導致服務中斷時長超過0.5-1s。
這種時候官方就建議把cms換成g1了。
最終cpu驟降,從99%降至一般運行狀態下的2%左右。好了。就這樣吧,雖然不希望線上出現這種問題,但一旦出問題了,搞一遍還蠻帶感的。期待下一次的摸排:DDDDD
後記:
最近在復盤這次垃圾回收,因為這次jvm排查的經歷讓我對垃圾回收機制和分代回收這一塊加深了印象。復盤的時候發現自己思考地還欠缺深入。這次的問題有兩個地方值得深思,一個是為什麼metaspace會超?如果正常進行GC,為何會超出閾值?其次是沒有調大metaspace的前提下,換成G1為什麼就沒有再頻繁FullGC過?
為什麼metaspace會超,我經過比對當時留下CMS的jstat數據和G1當前的數據,發現新生代CMS劃分區域非常大,是現在G1的十倍。G1是不建議設定新老年代比例的,這樣它就可以動態調整新老年代比例,達到最優實踐。首先說明metaspace是存儲類描述信息的,當堆上分配了對象,就會關聯到metaspace裡面的類信息,如果堆上的對象不回收,那麼metaspace裡面的類信息也不會回收。
在配置cms時,我們會設置新生代老年代比例,新生代空間是固定的,如果新生代的空間比較大,回收間隔時間長,那就會導致metaspace里的class信息無法被釋放,最終導致未進行youngGC而率先因為metaspace超了跑了FullGC。但G1的新生代空間是根據使用情況動態分配的,它的演算法會去回收最有回收價值的region。
就這次修改參數而言,使用G1比CMS時間短很多,就已經執行了717次youngGC,而CMS用了那麼久,才52次,側面也能佐證這個猜測的正確性,解釋了為何在相同metaspace的配置下,G1沒有產生頻繁FullGC的原因:新生代對象回收加快,metaspace空間得到了儘快釋放,沒有達到閾值,於是不會觸發FullGC。
參考文獻:
*
*
*
*
*
JVM調優jstack怎麼找出最耗cpu的線程並定位代碼
第一步:先找出java的進程Id(PID) 假設java應用名稱是zcg_commodity
ps -ef|grep zcg_commodity
得到進程Id為32464
第二步:找出該進程內最消耗CPU的線程
top -Hp pid
輸入top -Hp 32464
TIME列就是各個java線程耗費的CPU的時間,比如圖中是線程ID的為2012的線程,
通過 printf 「%x\n」 2012
得到2012的十六進位為 7dc
第三步:
一般會進到jdk的bin目錄下,root許可權執行
jstack 32464|grep 7dc
原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/156803.html