定位JVM內存溢出問題思路總結

JVM的內存溢出問題,是個常見而有時候有很是難以定位的問題。定位內存溢出問題常見方法有不少,可是其實不少狀況下可供你選擇的有效手段很是有限。不少方法在一些實際場景下沒有實用價值。這裏總結下個人一些定位思路。java

 

要定位JVM內存溢出問題,首先要對JVM的內存佈局有必定的瞭解,對常見的JVM內存工具要比較熟悉。所謂工欲善其事,必先利其器。而熟悉JVM的內存管理機制是你定位JVM內存問題的基石。首先介紹下JVM的內存管理機制:數據庫

JAVA程序和C類程序一個重要的區別就是JAVA中的內存回收管理工做有JVM完成,而不須要爲程序中的每一個new/malloc去delete/free。那JVM這麼智能怎麼還有內存溢出問題呢,一個常見的緣由是咱們的JAVA程序中長時間的持有了不應持有的對象,或者申請了過多的對象使得JVM的內存不夠用。windows

 

JVM管理的幾個內存區域包括如下幾個內存區域:tomcat

一、  方法區:用於存儲JAVA類信息、常量、靜態變量。這個區域也能夠發生垃圾回收,好比當一些類不在被引用時JVM能夠卸載這個類,不過這種回收動做不多發生。另外全部線程都共享方法區,所以線程對方法區的訪問被設計爲線程安全的。安全

二、  虛擬機棧:JAVA虛擬機棧是線程私有的,每當啓動一個新線程時,JVM都會爲它分配一個JAVA虛擬機棧。沒當線程調用方法時,JVM都會爲虛擬機棧壓入一個棧幀,該棧幀用於存儲參數、局部變量、中間運算結果、方法出口等。服務器

三、  本地方法棧:和虛擬機棧相似,只是他是專爲JAVA中的Native方法服務。當線程進入本地方法後,它已經脫離的JVM的限制,甚至能夠直接使用本地處理器中的寄存器。實際上本地方法的調用機制很是依賴於JVM的具體實現。網絡

四、  堆:由JVM啓動時建立,由全部線程共享,用於存放對象的實例。通常狀況下它是JVM中管理的內存中最大的一塊。絕大部分JVM內存問題都發生在這一塊。工具

五、  程序計數器:同虛擬機棧同樣,它也是線程私有,啓動線程是建立。程序計數器的中保存的內容老是下一條將被執行的指令的地址。佈局

 

這幾個內存區域,除了程序計數器區域外,其餘幾個區域都有可能發生內存溢出問題。常見的內存溢出有兩種:測試

一、方法區溢出。出現該問題時,JVM會報以下相似錯誤:java.lang.OutOfMemoryError : PermGenSpace ,Perm區的最大內存大小能夠經過-XX:MaxPermSize=指定。引發這類內存溢出緣由通常有兩個,一個是常量池太大,一個是須要加載的CLASS類太多。出現問題的時候排查下這兩種可能性,問題能夠很快找到。這類問題程序穩定後也不多出現。

二、堆內存溢出。在JVM可以使用的最大堆內存能夠在啓動的時候經過-Xmx參數指定。堆內存溢出是最爲常見的內存溢出問題,發生堆內存溢出時,JVM會報告以下錯誤:java.lang.OutOfMemoryError : java heap space。

這裏列舉下在定位堆內存溢出時,常見的方法和思路。堆內存溢出顧名思義就是,堆內存不夠用了,若是程序設計的最大對內存已經耗盡,那說明程序設計存在問題,不應申請不少內存的邏輯申請了不少的內存,該釋放的對象沒有釋放。最重要的問題是就要要找出究竟是什麼對象沒有及時釋放,致使佔用了過多的內存。常見的方法:

a、  一個強大的定位工具是使用 jprofiler,經過jprofiler能夠實時的監控到,當前的堆內存的整體使用狀況及當前存活的對象、大小、分配樹、對象引用鏈等等,功能很是全面。以下圖所示:

 

 

經過該工具還能夠實時的生成堆內存快照,而後經過不一樣時間點生成的內存快照作比較已肯定內存增加點。可是實際使用中若是程序征程運行中佔用的內存就比較高,好比800M左右,而在32位機器上,JVM可使用的最高內存在1280M左右,若是應用程序設置的最大堆內存是1024M,那麼實際即將發生堆內存溢出時,程序使用的內存是處一個比較高的位置。這時候經過jprofiler監控效果就很不理想,我在windows server 2003 32位服務器上作測試,想經過jprofiler監控一個TOMCAT應用程序,該應用程序常態中佔用內存在800M左右,啓用jprofiler監控後,jprofiler監控程序自己就會變得很不穩定,經常莫名掛死(經過jprofiler監控應用程序的時候實際上上jprofiler對應用程序又作了一層包裝後啓動的,所以jprofiler監控程序掛死,等於被監控的程序掛死)。很難抓取到有用的信息。

所以jprofiler在32位機器上只適用用小內存應用程序。

b、  使用JVM自帶的jmap工具導出堆信息分析,這個工具能夠經過以下命令處處自定的JVM進程的堆內存:jmap –dump:format=b,file=heap.bin <pid>  。
但這個工具也有同樣的問題,在應用程序使用的內存處於高位時,使用jmap導出堆信息會出現空間不足,沒法導出的問題。Java自帶的不少工具都有相似的限制,因此實際上你的選擇並很少。

c、  在啓動JVM的時候加上如下兩個參數:

 -XX:HeapDumpPath=./dumpfile.hprof

-XX:+HeapDumpOnOutOfMemoryError

表示出現OOM錯誤後,將堆信息dump出來。不過這個參數也有個問題,在實際使用的時候發如今windows平臺上,若是程序是以TOMCAT服務的方式運行,出現OOM錯誤後堆信息也dump不出來。

d、  經過visualVM程序監控JVM,JDK 1.6中自帶的可視化監控工具,這個工具比較實用,功能也比較強大。能夠監控線程信息,堆內存信息,還能夠實時導出堆信息以及強制GC。監控本地JVM直接啓動該工具後就能夠監控,遠程監控須要啓動jstatd和jrxml。

啓動jstatd方法:新建jstatd.all.policy文件,添加以下內容,tools.jar包的路徑根據實際狀況修改:

grant codebase "file:/home/ndmc/tomcat/jdk/jre/lib/tools.jar" {

   permission java.security.AllPermission;

};

執行啓動命令./jstatd -J-Djava.security.policy=jstatd.all.policy,默認端口爲1099,後面可跟-p指定其餘端口號

使用jrxml遠程監控JVM的時候須要加上如下參數:

-Dcom.sun.management.jmxremote

-Dcom.sun.management.jmxremote.port=8849

-Dcom.sun.management.jmxremote.ssl=false

-Dcom.sun.management.jmxremote.authenticate=false

-Djava.rmi.server.hostname=hostip

監控效果以下:

 

 


在監控過程當中能夠手動的GC和DUMP堆內存,還能夠設置Heap dump on OOME: enabled,使得在堆內存溢出的時候能夠dump heap。不過根據實際使用,建議最高在堆內存即將溢出的時候就應該手動dump heap,由於等到OOM的時候經常程序已經掛死,heap已經導不出來了。

e、  導出dump信息後就能夠經過jprofiler工具或者HeapAnalyzer作分析。這兩個工具都很強大,網上有不少的使用指導。能夠初步分析出究竟是什麼對象在佔用內存,以及相應的引用鏈,到這一步在結合源代碼在定位內存溢出問題就相對容易了。  

 

總結:定位內存溢出問題須要一個冷靜的頭腦、敏銳的觀察能力、縝密的分析問題能力。甚至經常須要根據一些現象作猜想而後驗證,找出最後的元兇,有可能內存的溢出是有多個方面引發的:代碼BUG、軟件設計問題、網絡的吞吐量以及網路鏈接問題、數據庫問題等均可能是相關須要排查的因素,甚至有時候多是操做系統或者JVM自己存在的BUG致使。而可以在JVM堆內存即將溢出的時候導出堆信息是問題定位的關鍵,只要可以導出堆信息,一系列的猜想、分析是否正確就有可靠的證據。--------------------- 原文:https://blog.csdn.net/xishanxinyue/article/details/15336551

相關文章
相關標籤/搜索