轉載自:https://www.cnblogs.com/ThinkVenus/p/6805495.htmlhtml
最近查找了不少關於OOM,甚至於Java內存管理以及JVM的相關資料,發現這方面的東西太多了,竟有一種眼花繚亂的感受,要想了解全面的話,恐非一篇文章能說清的,所以按照本身的理解整理了一篇,剩下的還須要繼續學習。java
1)什麼是OOM? OOM,全稱「Out Of Memory」,翻譯成中文就是「內存用完了」,來源於java.lang.OutOfMemoryError。看下關於的官方說明: Thrown when the Java Virtual Machine cannot allocate an object because it is out of memory, and no more memory could be made available by the garbage collector. 意思就是說,當JVM由於沒有足夠的內存來爲對象分配空間而且垃圾回收器也已經沒有空間可回收時,就會拋出這個error(注:非exception,由於這個問題已經嚴重到不足以被應用處理)。算法
2)爲何會OOM?
爲何會沒有內存了呢?緣由不外乎有兩點:
1)分配的少了:好比虛擬機自己可以使用的內存(通常經過啓動時的VM參數指定)太少。
2)應用用的太多,而且用完沒釋放,浪費了。此時就會形成內存泄露或者內存溢出。
內存泄露:申請使用完的內存沒有釋放,致使虛擬機不能再次使用該內存,此時這段內存就泄露了,由於申請者不用了,而又不能被虛擬機分配給別人用。
內存溢出:申請的內存超出了JVM能提供的內存大小,此時稱之爲溢出。
在以前沒有垃圾自動回收的日子裏,好比C語言和C++語言,咱們必須親自負責內存的申請與釋放操做,若是申請了內存,用完後又忘記了釋放,好比C++中的new了可是沒有delete,那麼就可能形成內存泄露。偶爾的內存泄露可能不會形成問題,而大量的內存泄露可能會致使內存溢出。
而在Java語言中,因爲存在了垃圾自動回收機制,因此,咱們通常不用去主動釋放不用的對象所佔的內存,也就是理論上來講,是不會存在「內存泄露」的。可是,若是編碼不當,好比,將某個對象的引用放到了全局的Map中,雖然方法結束了,可是因爲垃圾回收器會根據對象的引用狀況來回收內存,致使該對象不能被及時的回收。若是該種狀況出現次數多了,就會致使內存溢出,好比系統中常用的緩存機制。Java中的內存泄露,不一樣於C++中的忘了delete,每每是邏輯上的緣由泄露。
3)OOM的類型
JVM內存模型:
按照JVM規範,JAVA虛擬機在運行時會管理如下的內存區域:
- 程序計數器:當前線程執行的字節碼的行號指示器,線程私有
- JAVA虛擬機棧:Java方法執行的內存模型,每一個Java方法的執行對應着一個棧幀的進棧和出棧的操做。
- 本地方法棧:相似「 JAVA虛擬機棧 」,可是爲native方法的運行提供內存環境。
- JAVA堆:對象內存分配的地方,內存垃圾回收的主要區域,全部線程共享。可分爲新生代,老生代。
- 方法區:用於存儲已經被JVM加載的類信息、常量、靜態變量、即時編譯器編譯後的代碼等數據。Hotspot中的「永久代」。
- 運行時常量池:方法區的一部分,存儲常量信息,如各類字面量、符號引用等。
- 直接內存:並非JVM運行時數據區的一部分, 可直接訪問的內存, 好比NIO會用到這部分。
按照JVM規範,除了程序計數器不會拋出OOM外,其餘各個內存區域均可能會拋出OOM。
最多見的OOM狀況有如下三種:
- java.lang.OutOfMemoryError: Java heap space ------>java堆內存溢出,此種狀況最多見,通常因爲內存泄露或者堆的大小設置不當引發。對於內存泄露,須要經過內存監控軟件查找程序中的泄露代碼,而堆大小能夠經過虛擬機參數-Xms,-Xmx等修改。
- java.lang.OutOfMemoryError: PermGen space ------>java永久代溢出,即方法區溢出了,通常出現於大量Class或者jsp頁面,或者採用cglib等反射機制的狀況,由於上述狀況會產生大量的Class信息存儲於方法區。此種狀況能夠經過更改方法區的大小來解決,使用相似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,過多的常量尤爲是字符串也會致使方法區溢出。
- java.lang.StackOverflowError ------> 不會拋OOM error,但也是比較常見的Java內存溢出。JAVA虛擬機棧溢出,通常是因爲程序中存在死循環或者深度遞歸調用形成的,棧大小設置過小也會出現此種溢出。能夠經過虛擬機參數-Xss來設置棧的大小。
4)
OOM分析--heapdump
要dump堆的內存鏡像,能夠採用以下兩種方式:
- 設置JVM參數-XX:+HeapDumpOnOutOfMemoryError,設定當發生OOM時自動dump出堆信息。不過該方法須要JDK5以上版本。
- 使用JDK自帶的jmap命令。"jmap -dump:format=b,file=heap.bin <pid>" 其中pid能夠經過jps獲取。
dump堆內存信息後,須要對dump出的文件進行分析,從而找到OOM的緣由。經常使用的工具備:
- mat: eclipse memory analyzer, 基於eclipse RCP的內存分析工具。詳細信息參見:http://www.eclipse.org/mat/,推薦使用。
- jhat:JDK自帶的java heap analyze tool,能夠將堆中的對象以html的形式顯示出來,包括對象的數量,大小等等,並支持對象查詢語言OQL,分析相關的應用後,能夠經過http://localhost:7000來訪問分析結果。不推薦使用,由於在實際的排查過程當中,通常是先在生產環境 dump出文件來,而後拉到本身的開發機器上分析,因此,不如採用高級的分析工具好比前面的mat來的高效。
這個連接:http://www.ibm.com/developerworks/cn/opensource/os-cn-ecl-ma/index.html中提供了一個採用mat分析的例子 。
注意:由於JVM規範沒有對dump出的文件的格式進行定義,因此不一樣的虛擬機產生的dump文件並非同樣的。在分析時,須要針對不一樣的虛擬機的輸出採用不一樣的分析工具(固然,有的工具能夠兼容多個虛擬機的格式)。IBM HeapAnalyzer也是分析heap的一個經常使用的工具。
5)小結
涉及到的虛擬機的技術或者工具,每每須要考慮到虛擬機規範以及不一樣的虛擬機實現。尤爲是針對虛擬機調優時,每每須要針對虛擬機在某些方面的實現策略來考慮,好比,不一樣的虛擬機的垃圾回收算法是不同的,而這直接影響了虛擬機某些參數的設置,以達到虛擬機的最佳性能。
而針對JVM運行時的分析與診斷,則須要掌握分析基本方法,針對具體狀況,運用虛擬機的原理,具體分析。一句話,水很深啊。