一次JVM_OLD區佔用過高、頻繁Full GC的解決過程

王一洋發表於2018-10-31

最近,公司網站頻繁報警,JVM_OLD佔用過高,線上訪問超時嚴重,針對這個問題著實頭疼了一把,不過最終還是解決了,下面說下解決的過程。

1,首先 登到線上機器上去,top命令,檢視當前機器的負載,檢視當前哪個程式在消耗資源。

Shell

1

top

找到CPU或者記憶體佔用過高的那個程式。發現有一個程式,CPU居高不下,保留程式id

2,top命令,繼續跟蹤該程式裡的所有執行緒,找到佔用CPU過高的執行緒。

Shell

1

top -Hp [程式ID]

 

top-hp

3,找到執行緒ID,正在消耗CPU,把執行緒ID轉換為16進位制,執行jstack命令保留當前java程式堆疊

Shell

1

jstack [程式ID] > jstack_01

然後,在該檔案jstack_01 裡搜尋 執行緒id為[16進位制的執行緒id]的執行緒,檢視堆疊資訊。

jstack_01

一般情況下,這三部就能找到消耗資源的執行緒的情況,看到該執行緒的執行堆疊資訊,當時發現,其實中 nid=0x8d0f 的執行緒在佔用cpu,堆疊裡看到的是VM執行緒,VM執行緒的意思就是jvm執行緒在工作,看下面幾個run的執行緒,都是gc執行緒,猜測,此時,jvm正在GC。

4、接下來就是確認當前JVM各分割槽的情況了,這裡使用的是jstat

Shell

1

jstat -gcutil [程式id] 1000

每1000ms列印一次gc資訊。

jstat_01

通過此圖,可以發現,新生代eden區,survivor區,old區都已經100%,並且,jvm啟動到現在,Full GC了978次,共花費時間12240.255秒,這樣平均每次Full GC 12.5秒。

看來就是Full GC頻繁並且過程漫長,導致線上機器卡死的問題了。下面解釋下jstat的gc資訊各個欄位的含義:

Shell

1

2

3

4

5

6

7

8

9

10

S0 年輕代中第一個survivor(倖存區)已使用的佔當前容量百分比

S1 年輕代中第二個survivor(倖存區)已使用的佔當前容量百分比

E 年輕代中Eden(伊甸園)已使用的佔當前容量百分比

O old代已使用的佔當前容量百分比

P perm代已使用的佔當前容量百分比

YGC 從應用程式啟動到取樣時年輕代中gc次數

YGCT 從應用程式啟動到取樣時年輕代中gc所用時間(s)

FGC 從應用程式啟動到取樣時old代(全gc)gc次數

FGCT 從應用程式啟動到取樣時old代(全gc)gc所用時間(s)

GCT 從應用程式啟動到取樣時gc用的總時間(s)

5、確認是Full GC問題,那就只能看堆記憶體了,

Shell

1

jmap -histo [程式id] > jmap.txt

 

該命令可以檢視當前jvm記憶體裡物件的例項數和佔用記憶體數

【注意】該命令可能會造成線上程式假死,會卡死一段時間,所以線上程式謹慎使用!!!

jmap_01

從結果裡可以看出,佔記憶體最多的是前兩個,一個是char,一個是string

所以有以下猜測:

1)程式裡有記憶體快取,快取的是字串,記憶體快取逐漸增多,逐步移步老年代,最終導致爆滿。

2)有大量拼接字串的地方,

3)static的變數,儲存大量的字串,排名第六的是hashMap,猜想可能是有static的 hashMap??

繼續分析該檔案,看到,有個 com.bj58.jwdf.framework.cache.ConcurrentCacheProvider 物件,有10w多個例項,通過看程式碼,的確這個是一個自己實現的記憶體快取,果斷想辦法優化。

優化上線後,發現記憶體問題雖然有所緩解,但是還是會有jvm爆滿的時候。

這樣在繼續分析,就比較困難了,然後同時優化了下jvm啟動的引數,新增full gc的時機,

 

Shell

1

-XX:CMSInitiatingOccupancyFraction=80 -XX:+UseCMSInitiatingOccupancyOnly

保證,在old區達到80%的時候就開始執行Full GC。

新增:

Shell

1

-XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log

列印gc資訊,以便保留證據。

並且在程式碼裡查詢一些其他的公共的static 的map 以及 記憶體快取的地方,統統優化了,上線後還是不理想。

最後沒辦法了,只能dump堆記憶體快照了,用工具分析了。

修改jvm啟動記憶體,改小一些,以便在滿的時候打的快照能在本地分析,,

Shell

1

jmap -dump:format=b,file=aa.bin 1232134

改命令列印出堆記憶體快照,我把jvm記憶體改成最大6G,下載的快照檔案,足足有6.5G,然後下載MAT工具進行分析。

 

http://www.liuzk.com/294.html

相關文章