現象分析:
我們有一個生產服務,規模是12臺機器*6個節點 = 72個節點的服務,最近老是出現某個節點突然掛掉的情況,問題出現頻繁,一天需要重啓很多個節點
查看tomcat日誌,發現是堆內存溢出
使用jmap -heap pid
查看各個JVM
內存空間的使用情況發現,所有的內存都被佔滿了,如下圖所示:
發現堆內存的確都被吃完了
使用top查看各個pid
吃資源的情況後發現OOM
的節點的CPU資源佔用特別高,基本都在100%以上
瘋狂吃CPU推斷應該是在JVM
在進行fullGC
(回收老年代,在進行fullGC
的時候,程序不提供任何其他服務),使用jstat -gc pid
查看GC
情況
其中每個字段的意思對應如下
S0C:第一個倖存區的大小
S1C:第二個倖存區的大小
S0U:第一個倖存區的使用大小
S1U:第二個倖存區的使用大小
EC:伊甸園區的大小
EU:伊甸園區的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法區大小
MU:方法區使用大小
CCSC:壓縮類空間大小
CCSU:壓縮類空間使用大小
YGC:年輕代垃圾回收次數
YGCT:年輕代垃圾回收消耗時間
FGC:老年代垃圾回收次數
FGCT:老年代垃圾回收消耗時間
GCT:垃圾回收消耗總時間
看到其中fullGC
的次數非常高,達到200-9000次,時間高達以小時計數,所以推斷應該是有大對象在內存中,並且一直在被引用
使用jmap -histo pid
查看哪些對象佔用了空間發現char對象佔據了特別大的空間
在程序OOM
的時候使用jmap -dump:live,format=b,file=tai.hprof pid
這個命令dump下當時的內存文件到磁盤中。使用gzip
壓縮文件(VPN
限速太慢,時間可能很長)並且拉去到本機上
使用eclipse的插件分析dump文件tai.hprof
發現其中一個異常,一個線程對象持有了大量內存空間
點擊see stacktrace
查看對應的堆棧信息
發現最後一步在程序中的堆棧信息是這一行代碼是最後執行代碼
查看內存佔用發現一個String對象使用內存非常多,並且結構有點像第三方返回對象
將值存儲到對應文件中發現這個字符串大小爲660MB
,其中的值截取如下:
結果:結果是發現是調用下層的Go項目返回的HTTP response中返回的對象太大,經過各種裝換之後,一個線程持有的內存達到了4G多,而一個tomcat進程分配的內存總共也就5G,造成的OOM。經過排查Go項目。的確有一個地方有bug,存在笛卡爾積,在數據條數100+條列表的時候產生的對象會產生大對象的String返回到我們這個生產服務 。