最近線上某站點young gc有點頻繁,經過排查確定了問題,這里記錄一下
遇到GC,三步走
1. 確認是哪些對象引起的GC
2. 找到這些對象是哪里構造的
3. 根據情況進行優化。
一般情況下,我們可以通過jmap dump的方式把堆dump出來進行分析,但dump出來的是一個靜態文件。反應的是某一時刻的情況, young gc 中的對象存活時間較短,dump出的文件不一定能反應出問題,所以這里我使用了jmap histo(注意:這里的jmap histor只是顯示對象的個數和大小,如果要知道對象屬性的值,還是要dump)
jmap -histo pid #如果使用jmap -histo:live pid 則會觸發一次full gc
這個命令會顯示Java堆中的對象的個數和總大小,並且按照大小排列。 這里我們並不需要這么多。
jmap -histo pid| sed -n '3,23p'
這里只取了前20條, 這個命令只能反應某一時刻的情況,如果想要持續觀察
watch -n 5 -d 'jmap -histo pid | sed -n '3,23p''
上面的命令 每5S刷新一次,可以看到有變化的對象
watch -n 5 -d 'jmap -histo pid | sed -n '3,23p' >> obj.log'
上面的命令是將數據記錄到文件,我們可以讓命令持續2分鍾(盡量保證能經歷2次GC),然后從服務器上拿到這個文件,寫一個python腳本進行解析,解析出來后,導出到excel ,並用透視圖繪制成折線圖。
這里基本就可以看出來會被GC的對象。
找出了被GC的對象,接下里就是找出這些對象在哪里構造的,如果這個對象只有一個地方被構造,則可以直接定位到,如果在多個地方被構造,則需要找出被構造最多次的地方。 這里直接借助阿里的Arthas。
stack xx.xx <init> #<init> 表示構造函數
這里就是找出哪里在調用該類的構造函數,並且打印出堆棧,結果可能會很多,導出來,根據堆棧信息,找出top1, 可以導出多次,最后匯總。
這里根據堆棧信息找到了代碼,根據代碼,分析了上下文,發現有些對象可以復用, 這里采用了享元模式, 把其中一些對象改為支持單例模式,至此結束。
當然根據實際情況, 優化的方式不限於代碼,也可以調整一些JVM參數,比如新生代的內存大小等。