背景
乙個服務突然所有機器開始頻繁full gc。而服務本身沒有任何改動和發布記錄。上線檢視gc log日誌,日誌如下:
從日誌來看,每次發生full gc的時候都比較奇怪,主要有兩點,第一、old區域和perm的區域使用率很低,沒有到達觸發full gc的條件,第
二、專案中配置的是cms,為什麼沒有進行 cms gc,直接進行了full gc呢。
查詢過程
第一、**會不會是呼叫了system.gc()
考慮在使用direct memory的時候,先判斷direct memory是否足夠,要是不足的話會使用system.gc()嘗試釋放記憶體。於是直接使用反射去監控direct memory。發現direct memory的使用率始終在10%左右,不可能去呼叫system.gc()。
而且此時去檢視jvm引數已經禁止顯示呼叫了system.gc()了。
第二、使用 jstat -gccause檢視gc原因
想著要是能找到gc的原因就好了。於是使用 jstat -gccause實時監控gc原因,但是發現始終是allocation failure。但是在監控中發現old區域中有突然增加800m,通過我司的監控平台也發現了old區域暴漲的現象。監控如下:
並且通過jmap -histo pid檢視old gen 突變前後記憶體增加值,發現增加的800m全部是byte,並且dump記憶體下來使用mat檢視記憶體,然後並沒有什麼收穫。
第三、找到有問題開始時候的改動點
因為專案在發生問題的時候並沒有改動和上線,基本上就排除**本身的原因。聯絡運維告知那個時間點,我們所在的服務節點上部署了log_agent。
log_agent的作用就是把本地日誌上報到日誌中心儲存起來,其架構示意圖demo如下:
猜著肯定是程式設計客棧和log_agent通訊的時候有bug導致的,於是讓運維幫忙把其中一台機器上的log_agent給刪除了,刪除之後full gc恢復正常。
到此基本上確定了是日誌上報導致的問題。
第四、定位日誌上報的jar具體有問題的**
定位到是日誌上報的jar導致的問題之後,就把這個問題反饋給了相關負責人。但是他們追蹤了很久之後並沒有發現什麼問題。
之後有時間之後,我就把他們相關**看了一下,發現其中有段**有點問題。有問題**如下:
在出入log的的時候在append中會呼叫sendlogentry這個方法,而logentries本身是個list物件,非執行緒安全的。這樣的話,在多個執行緒中同時輸出日誌就有安全問題。於是就在sendlogentry這個方法上加上執行緒安全(synchronized),上線問題解決,沒有發生頻繁full gc了。
但是多執行緒下同時呼叫list也不應該頻繁full gc啊,這個地方有bug,但是不應該導致頻繁 full gc。我懷疑是client.log(logentries); 這個方法本身不是執行緒安全的。以為我把執行緒同步塊鎖在了client.log(logentries);這個**塊上。發現問題也得以解決。
client.log的**就是乙個傳送相關日誌、並接收返回值進行確認,使用的是thrift框架進行通訊的。於是在接收返回值的地方,給加了點log。**如下:
從日誌中我們可以看wvolvskfb到,從返回值中讀取的位元組流大小最大達1.2g甚wvolvskfb至1.8g,這很明顯不正常啊。因為young gen 1.5g,old gen 1g,必定會拋oom。而在最上層捕獲了error,但是預設情況下卻沒有log,導致log中看不出任何問題。
回想起我司rpc服務也是用的thrift是用的連線池的方式,所以client肯定是非執行緒安全的。
問題定位到之後,準備反饋給那個人。發現那個人已經離職了。於是嘗試公升級到最新的jar之後,發現他們在sendlogentry這個方法上加上了synchronized。
總結上面給出了總結後應該遵循的定位問題步驟。真實的查詢過程絕不是按照上面的那個過程來的,這個問題的追查持續了大概兩周(每天投入1-2個小時左右吧?)。
主要有兩個坑:
gc log。開始的時候關注點一直在gc log上。從gc log來看根本不滿足發生full gc的條件。於是專注點在認為引入的jar有在調sywww.cppcns.comstem.gc()並沒有注意到這個-xx:+disableexplicitgc引數
對error的處理。我司日誌中心提供程式設計客棧的jar居然直接忽略了error導致了oom日誌一直沒有顯示出來,不然問題發生時肯定就能直接定位到了。
jvm丟擲oom之後,就算配置的是cms,jvm仍舊是使用的full gc來**記憶體。因為cms會有記憶體碎片化問題,已經發生了oom,可能是因為沒有連續記憶體存放新申請的物件,full gc沒有記憶體碎片的問題,所以直接使用full gc**的策略是合理的。
本文標題: 一次詭異的full gc查詢問題全過程
本文位址:
一次線上FullGC問題記錄
標題採自 英雄聯盟 瑞文 斷劍重鑄之日,騎士歸來之時!前兩天早上在擠地鐵的時候看到小組群裡,主管發了好多訊息,開啟來一看,說是xx專案自從22號發版後,每天晚上就瘋狂full gc,讓我們查一下什麼原因,嘻嘻嘻,一開始聽到,心裡竊喜,為什麼呢。因為自己以前對jvm也有些了解,不過都只是紙上談兵罷了。...
一次詭異的 SQL 數量統計查詢不准的問題
ps 個人部落格原文 一次詭異的 sql 數量統計查詢不准的問題 乙個統計數量 有連表 的 sql,加上條件若干假設結果為 n1,然後再加條件詳細統計細分數量,詭異的來了,假設加的條件分別是 t1.f1 t2.f2 和 t1.f1 t2.f2,這兩個細分 sql 的結果 n2 和 n3 加起來竟然不...
記錄一次使用mockjs出現詭異問題
根據需要,專案引入了mock,一切就安裝就緒妥當之後,發現專案部分功能模組異常 這個功能模組使用了three的gltfloader載入器,報錯是在gltfloader裡面丟擲的 丟擲的異常 快速定位問題,發現是轉json的時候報錯 繼續看呼叫棧,找到這個引數的 發現gltfloader的原理是用原生...