OOM淺析
相信有一定java開發經驗的人或多或少都會遇到OutOfMemoryError的問題,這個問題曾困擾了我很長時間,隨著解決各類問題經驗的積累以及對問題根源的探索,終於有了一個比較深入的認識。參照網上的一些解決方案,在這裡加以整理。
在解決java記憶體溢位問題之前,需要對jvm(java虛擬機器)的記憶體管理有一定的認識。jvm管理的記憶體大致包括三種不同型別的記憶體區域:
Permanent Generation space(永久 儲存區域)、Heap space(堆區域)、Java Stacks(Java棧)。
永久儲存區域主要存放Class(類)和Meta的資訊,Class第一次被Load的時候被放入PermGen space區域,Class需要儲存的內容主要包括方法和靜態屬性。
堆區域用來存放Class的例項(即物件),物件需要儲存的內容主要是非靜態屬性。每次用new建立一個物件例項後,物件例項儲存在堆區域中,這部分空間也被jvm的垃圾回收機制管理。
Java棧跟大多數程式語言包括組合語言的棧功能相似,主要基本型別變數以及方法的輸入輸出引數。Java程式的每個執行緒中都有一個獨立的堆疊。
容易發生記憶體溢位問題的記憶體空間包括:Permanent Generation space和Heap space。
第一種OutOfMemoryError: PermGen space
發生這種問題的原意是程式中使用了大量的jar或class,使java虛擬機器裝載類的空間不夠,與Permanent Generation space有關。解決這類問題有以下兩種辦法:
1. 增加java虛擬機器中的XX:PermSize和XX:MaxPermSize引數的大小,其中XX:PermSize是初始永久儲存區域大小,XX:MaxPermSize是最大永久儲存區域大小。
如針對tomcat6.0,在catalina.sh 或catalina.bat檔案中一系列環境變數名說明結束處(大約在70行左右) 增加一行:JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m"
如果是windows伺服器還可以在系統環境變數中設定。感覺用tomcat釋出sprint+struts+hibernate架構的程式時很容易發生這種記憶體溢位錯誤。使用上述方法,我成功解決了部署ssh專案的tomcat伺服器經常宕機的問題。
2. 清理應用程式中web-inf/lib下的jar。
如果tomcat部署了多個應用,很多應用都使用了相同的jar,可以將共同的jar移到tomcat共同的lib下,減少類的重複載入。這種方法是網上部分人推薦的,我沒試過,但感覺減少不了太大的空間,最靠譜的還是第一種方法。
第二種OutOfMemoryError: Java heap space
發生這種問題的原因是java虛擬機器建立的物件太多,在進行垃圾回收之間,虛擬機器分配的到堆記憶體空間已經用滿了,與Heap space有關。解決這類問題有兩種思路:
1. 檢查程式,看是否有死迴圈或不必要地重複建立大量物件。找到原因後,修改程式和演算法。
2. 增加Java虛擬機器中Xms(初始堆大小)和Xmx(最大堆大小)引數的大小。如:set JAVA_OPTS= -Xms256m -Xmx1024m
第三種OutOfMemoryError:unable to create new native thread
這種錯誤在Java執行緒個數很多的情況下容易發生,我暫時還沒遇到過,發生原意和解決辦法可以參考:
http://hi.baidu.com/hexiong/blog/item/16dc9e518fb10c2542a75b3c.html
參考自:http://blog.sina.com.cn/s/blog_701c951f0100n1sp.html
程式碼中如何檢視JVM記憶體設定資訊
1、Runtime.getRuntime().maxMemory();
最大可用記憶體,對應-Xmx;為JVM的最大可用記憶體,可通過-Xmx設定,預設值為實體記憶體的1/4,設值不能高於計算機實體記憶體;
2、Runtime.getRuntime().freeMemory();
當前JVM空閒記憶體;其為當前JVM空閒記憶體,因為JVM只有在需要記憶體時才佔用實體記憶體使用,所以freeMemory()的值一般情況下都很小,而JVM實際可用記憶體並不等於freeMemory(),而應該等maxMemory()-totalMemory()+freeMemory().及其設定JVM記憶體分配。
3、Runtime.getRuntime().totalMemory();
當前JVM佔用的記憶體總數,其值相當於當前JVM已使用的記憶體及freeMemory()的總和。為當前JVM佔用的記憶體總數,其值相當於當前JVM已使用的記憶體及freeMemory()的總和,會隨著JVM使用記憶體的增加而增加;
修改了這些配置沒有用,怎麼辦?
1、修改ini檔案
在eclipse.ini檔案中將預設設定改為-vmargs -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M或者更大,這要看你自己機器的記憶體配置而定;
2、在IDE中修改配置
開啟eclipse,選擇Window--Preferences...在對話方塊左邊的樹上雙擊Java,再雙擊Installed JREs,在右邊選擇前面有對勾的JRE,再單擊右邊的“Edit”按鈕,出現一個 Edit JRE 的對話方塊,在其中的Default VM Arguments: 框中輸入 -Xms128m -Xmx512m ,這樣設定Java擬虛機記憶體使用最小是128M,最大是512M,再單擊“OK”關閉 Edit JRE 對話方塊,再單擊“OK”關閉 Preferences對話方塊;
3、在快捷方式標籤下修改
如果這樣還解決不了,就右擊eclipse快捷方式,在屬性---快捷方式標籤下---目標中輸入D:\eclipse\eclipse.exe -clean -vmargs -Xms128M -Xmx512M -XX:PermSize=64M -XX:MaxPermSize=128M,其中D:\eclipse\eclipse.exe是eclipse的位置,就可以了。
參考自:http://www.360doc.com/content/12/0504/18/1527319_208660024.shtml
補充各引數含義
-vmargs:說明後面是VM(即Myeclipse虛擬機器)的引數
-Xms128m:虛擬機器佔用系統的最小堆記憶體為128M
-Xmx512m:虛擬機器佔用系統的最大堆記憶體為512M
-XX:PermSize=128m:最小堆大小(最小永久儲存區域)。一般報記憶體不足時,都是說這個太小,堆空間剩餘小於5%就會警告,建議把這個稍微設大一點,不過要視自己機器記憶體大小來設定。
-XX:MaxPermSize=256m:最大堆大小(最大永久儲存區域)。這個也適當大些。
注意: -Xmx512M的5%為25.6M,理論上要求-Xmx的數值與-XX:MaxPermSize必須大於25.6M.
所以,按照上面的方法適當進行修改,下面是我電腦上配置的:
-vmargs
-Xms128m
-Xmx512m
-XX:PermSize=64m
-XX:MaxPermSize=128m
-XX:ReservedCodeCacheSize=64m
如何監視jvm記憶體?使用jvisualvm
不能一味加記憶體解決問題,最好定期檢查一下程式對記憶體的消耗,以避免潛 在的記憶體溢位。建議大家使用jvisualvm來監控JVM。(JDK自帶,命令列直接打jvisualvm即可),下面是工具截圖,參考自:
http://my.oschina.net/zmen/blog/108989