愛伊米

當你的程式發生記憶體洩漏,該如何快速定位?

當你的程式發生記憶體洩漏,該如何快速定位?

導讀:我們都知道,Java和C++的最大區別是前者會自動收回不再使用的記憶體,後者需要程式設計師手動釋放。在c++中,如果我們忘記釋放記憶體就會發生記憶體洩漏。但是,不要以為jvm幫我們回收了記憶體就不會出現記憶體洩漏。

程式發生記憶體洩漏後,程序的可用記憶體會慢慢變少,最後的結果就是丟擲OOM錯誤。發生OOM錯誤後可能會想到是記憶體不夠大,於是把-Xmx引數調大,然後重啟應用。這麼做的結果就是,過了一段時間後,OOM依然會出現。最後無法再調大最大堆記憶體了,結果就是隻能每隔一段時間重啟一下應用。

記憶體洩漏的另一個可能的表現是請求的響應時間變長了。這是因為頻繁發生的GC會暫停其它所有執行緒造成的。

為了模擬這個場景,繼續使用昨天的程式碼做下小的調整

當你的程式發生記憶體洩漏,該如何快速定位?

執行引數是,把可用記憶體調小一點,並且在發生gc時輸出資訊,執行結果如下

當你的程式發生記憶體洩漏,該如何快速定位?

當你的程式發生記憶體洩漏,該如何快速定位?

可以看到雖然一直在gc,佔用的記憶體卻越來越多,說明程式有的物件無法被回收。但是上面的程式物件都是定義在方法內的,屬於區域性變數,區域性變數在方法執行結果後,所引用的物件在gc時應該被回收啊,但是這裡明顯沒有。

為了找出到底是哪些物件沒能被回收,我們加上執行引數,意思是發生OOM時把堆記憶體資訊dump出來。執行程式直至異常,於是得到heap。dump檔案。

當你的程式發生記憶體洩漏,該如何快速定位?

執行程式找到對應檔案

使用jdk自帶的jvisualvm。exe程式來檢視記憶體溢位檔案

當你的程式發生記憶體洩漏,該如何快速定位?

檢視類的例項,其實線上的問題大部分都是例項沒有被合理回收導致GC,當然這個案例中不是。

當你的程式發生記憶體洩漏,該如何快速定位?

上面只是其中一種處理方法

在線上的應用,記憶體往往會設定得很大,這樣發生OOM再把記憶體快照dump出來的檔案就會很大,可能大到在本地的電腦中已經無法分析了(因為記憶體不足夠開啟這個dump檔案)。這裡介紹另一種處理辦法:

簡單修改下程式

當你的程式發生記憶體洩漏,該如何快速定位?

用jps定位到程序號

因為已經知道了是哪個應用發生了OOM,這樣可以直接用jps找到程序號79917

用jstat分析gc活動情況

jstat是一個統計java程序記憶體使用情況和gc活動的工具,引數可以有很多,可以透過jstat -help檢視所有引數以及含義

上面是命令意思是輸出gc的情況,輸出時間,每8行輸出一個行頭資訊,統計的程序號是83012,每1000毫秒輸出一次資訊。

輸出資訊是Timestamp是距離jvm啟動的時間,S0、S1、E是新生代的兩個Survivor和Eden,O是老年代區,M是Metaspace,CCS使用壓縮比例,YGC和YGCT分別是新生代gc的次數和時間,FGC和FGCT分別是老年代gc的次數和時間,GCT是gc的總時間。雖然發生了gc,但是老年代記憶體佔用率根本沒下降,說明有的物件沒法被回收(當然也不排除這些物件真的是有用)

用jmap工具dump出記憶體快照

這時會得到記憶體快照檔案,就可以按照上面使用jdk自帶的jvisualvm。exe程式來檢視記憶體溢位檔案。

總結

以上嚴格地說還算不上jvm的調優,只是用了jvm工具把程式碼中存在的問題找了出來。我們進行jvm的主要目的是儘量減少停頓時間,提高系統的吞吐量。但是如果我們沒有對系統進行分析就盲目去設定其中的引數,可能會得到更壞的結果,jvm發展到今天,各種預設的引數可能是實驗室的人經過多次的測試來做平衡的,適用大多數的應用場景。如果你認為你的jvm確實有調優的必要,也務必要取樣分析,最後還得慢慢多次調節,才有可能得到更優的效果。

- END -

往期推薦

線上程式cpu佔用過高、程式死鎖,該如何定位問題?

線上伺服器cpu 100%了,該如何排查問題?

JVM問題定位 | 如何透過Arthas檢視當前系統的實時監控資料?

JDK原始碼閱讀 | 聊一聊GC收集器與記憶體分配策略

阿里人都在使用的線上診斷工具—Arthas

分享、點贊、在看

,給個

3連擊