記錄一次jvm內存泄露的問題


  前些天,運維告訴我剛上線的java服務占用CPU過高。

       以下是發現解決問題的具體流程。

   1:通過#top命令查看,我的java服務確實把CPU幾乎占滿了,如圖

   

   可看到18400這個進程CPU占用達到了1200%,這確實不太正常,那么我們接下來分析到底哪些線程占用了CPU

 

  2:通過#top -Hp 18400這條命令我們可以看到這個進程中線程的情況,部分截圖如下。

   

  通過截圖可以看到,前面的線程占用的CPU是比較高的,那我們就具體分析這些線程 

 

  3:我們通過#jstack 18400>18400.txt命令將這個java進程的線程棧給抓出來,可以多抓幾次做個對比。

  我們以18414這個線程為例,將它轉成16進制,linux可以用終端通過命令#printf "%x" 18414將線程id轉為16進制為47ee,那么我們接下來在文件里找47ee這個線程在干什么,部分截圖如下

  

   我們可以看到47ee是個垃圾回收線程;我們對其他占用CPU高的線程做相同的操作,發現都是GC線程。說明這個java服務一直在GC,這很不正常。那么我們接下來分析GC情況。

 

  4:我們通過命令#jstat -gcutil 18400 1000 100來查看接下來的GC情況,部分截圖如下

    

   是不是很直觀,通過YGC這一列發現younggc次數沒有增加,但是通過FGC這一列看到fullgc的次數一直在增加,可怕的是老年代並沒有回收(通過O這列看出來)。

  這時候你是不是想起來了一個名詞:內存泄露。沒錯,接下來我們就需要分析哪里出現了內存泄露。

 

  5:我們可以通過#jmap -dump:live,format=b,file=18400.dump 18400將這個進程當前的堆給dump下來。注意,這個文件可以看成是堆的快照,所以當前堆有多大,dump下來差不多也有多大。我dump下來的差不多2G。

  有了這個文件,我們需要分析,你可以用命令jhat分析,當然我們常用的是功能比較強大的圖形化工具,如JDK自帶的visualvm,也可以用第三方的JProfiler(我用的是這個),如果你用Eclipse,也可以安裝MAT插件。這些工具都能分析堆dump文件。

  需要注意的是,由於dump文件可能比較大,所以所需分析工具的內存也比較大,最好在性能比較好的機器上進行分析。

  下面是我的JProfiler分析的部分截圖

  

 

   是不是很直觀,有個對象占了97%的內存。那么接下來需要分析這個對象在哪產生,在哪被引用。我這里很明顯是這個LinkedList占用了全部空間,那么就去分析這個LinkedList里面都存了些什么,這些有可能需要結合你的代碼,我就不細說了。

  我分析出來的是ElasticSearch的客戶端工具JestClient的異步請求隊列太長了,整個List里面的節點都是異步請求信息,大概生成了10多萬個。消費不及時又無法被回收,所以產生了內存泄露。(注意,使用線程池也有可能會出現這個情況)

 

  6:分析出了原因,那么接下來就解決問題。因為我當時急着上線,不知道JestClient的異步隊列長度怎么配,就暫時把異步改成了同步,暫時解決了這個問題。上線后查看CPU,垃圾回收等情況確實恢復了正常。

  總的來說,上面的6步是一個完整的分析解決jvm虛擬機內存泄露的流程,當然可能有不完善的地方,但大體思路是沒錯的。

  通過這篇文章,我們可以總結出以下幾點:

  1:如何分析Java服務占用CPU過高的問題

  2:使用Java各種隊列的時候一定要關注隊列的長度,預防內存泄露。

  3:最好熟悉一下jvm的內存模型

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM