jstack函數cpu,JStack

本文目錄一覽:

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

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-11-18 01:57
下一篇 2024-11-18 01:57

相關推薦

  • Python中引入上一級目錄中函數

    Python中經常需要調用其他文件夾中的模塊或函數,其中一個常見的操作是引入上一級目錄中的函數。在此,我們將從多個角度詳細解釋如何在Python中引入上一級目錄的函數。 一、加入環…

    編程 2025-04-29
  • Python中capitalize函數的使用

    在Python的字元串操作中,capitalize函數常常被用到,這個函數可以使字元串中的第一個單詞首字母大寫,其餘字母小寫。在本文中,我們將從以下幾個方面對capitalize函…

    編程 2025-04-29
  • Python中set函數的作用

    Python中set函數是一個有用的數據類型,可以被用於許多編程場景中。在這篇文章中,我們將學習Python中set函數的多個方面,從而深入了解這個函數在Python中的用途。 一…

    編程 2025-04-29
  • 單片機列印函數

    單片機列印是指通過串口或並口將一些數據列印到終端設備上。在單片機應用中,列印非常重要。正確的列印數據可以讓我們知道單片機運行的狀態,方便我們進行調試;錯誤的列印數據可以幫助我們快速…

    編程 2025-04-29
  • 三角函數用英語怎麼說

    三角函數,即三角比函數,是指在一個銳角三角形中某一角的對邊、鄰邊之比。在數學中,三角函數包括正弦、餘弦、正切等,它們在數學、物理、工程和計算機等領域都得到了廣泛的應用。 一、正弦函…

    編程 2025-04-29
  • Python3定義函數參數類型

    Python是一門動態類型語言,不需要在定義變數時顯示的指定變數類型,但是Python3中提供了函數參數類型的聲明功能,在函數定義時明確定義參數類型。在函數的形參後面加上冒號(:)…

    編程 2025-04-29
  • Python實現計算階乘的函數

    本文將介紹如何使用Python定義函數fact(n),計算n的階乘。 一、什麼是階乘 階乘指從1乘到指定數之間所有整數的乘積。如:5! = 5 * 4 * 3 * 2 * 1 = …

    編程 2025-04-29
  • Python定義函數判斷奇偶數

    本文將從多個方面詳細闡述Python定義函數判斷奇偶數的方法,並提供完整的代碼示例。 一、初步了解Python函數 在介紹Python如何定義函數判斷奇偶數之前,我們先來了解一下P…

    編程 2025-04-29
  • CPU爆滿怎麼解決 Java為中心

    在Java編程中,難免會遇到CPU佔用過高的情況,接下來從多個方面介紹如何解決CPU爆滿問題。 一、優化代碼 1、減少循環次數。循環體內不要放太多邏輯判斷和計算,可以把計算提取出來…

    編程 2025-04-29
  • 分段函數Python

    本文將從以下幾個方面詳細闡述Python中的分段函數,包括函數基本定義、調用示例、圖像繪製、函數優化和應用實例。 一、函數基本定義 分段函數又稱為條件函數,指一條直線段或曲線段,由…

    編程 2025-04-29

發表回復

登錄後才能評論