Java之OutOfMemoryError簡單分析java
最近編碼遇到了Java內存溢出的問題,因此就想順便總結一下幾種致使Java內存溢出的栗子,以及碰到Java內存溢出要如何去解決。多線程
Java堆溢出
框架
Java堆用於存儲對象實例,只要不斷的建立對象,而且保證GC Roots到對象之間有可達的路徑來避免垃圾回收機制清除這些對象,那麼在對象數量到達最大堆容量限制以後就會產生內存溢出的異常。jsp
下面經過一段代碼進行堆內存溢出異常的測試。 工具
1 public class HeapOOM{ 2 static class HeapOOM{ 3 } 4 public static void main(String[] args) 5 { 6 List<OOMObject> list = new ArrayList<OOMObject>(); 7 While(1) 8 { 9 list.add(new OOMObject()); 10 } 11 } 12 }
代碼中對Java堆的限制大小是20M,不可擴展。具體設置是在VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError。
測試
當有堆溢出異常時,在異常堆棧信息欄中會出現「java.lang.OutOfMemoryError」,而後會提示:「Java heap space」。編碼
解決思路:spa
通常是經過內存映象分析工具對Dump出來的堆轉儲快照進行分析,重點是確認內存中對象是不是必要的,也就是先分清楚是Memory Leak(內存泄露)仍是Memory OverFlow(內存溢出)。若是是內存泄露,能夠經過工具檢查泄露對象到GC Roots的引用鏈。而後就能進而定位出泄露代碼的位置。
操作系統
若是不是內存泄露,也就是說內存中的對象都存活。那就檢查虛機的參數,與物理機進行對比,看看是否能夠調大,從代碼檢查是否存在生命週期過長的對象,嘗試減小程序運行期間內存的消耗。線程
虛擬機棧和本地方法棧溢出
因爲在HotSpot虛擬機中並不區分虛擬機棧以及本地方法棧,所以雖然-Xoss(用於設置本地方法棧的大小)參數存在,可是其實是無效的。棧容量只由-Xss參數設定。
下面是虛擬機和本地方法棧OOM的測試代碼:
1 public class JavaVMStackSOF{ 2 private int stackLength = 1; 3 public void stackLeak() { 4 stackLength++; 5 stackLeak(); 6 } 7 public static void main(String[] args) { 8 JavaVMStackSOF com = new JavaVMStackSOF(); 9 try{ 10 com.stackLeak(); 11 } 12 catch (Throwable e){ 13 System.out.println("Stack Length" + com.stackLength); 14 throw e; 15 } 16 } 17 }
上述代碼使用了-Xss=128k,減小了棧內存的容量,結果拋出StackOverflowError異常,異常出現時輸出的堆棧深度相應的縮小。
定義了大量的本地變量,增大此方法幀中本地變量表的長度,結果拋出StackOverflowError異常時輸出的堆棧深度相應縮小。也就是說在單個線程下,不管是因爲棧幀太大仍是虛擬機棧容量過小,當內存沒法分配時,都會拋出StackOverflowError異常。
解決思路:
若是使用虛擬機默認參數,棧深度在大多數狀況下達到1000-2000沒問題,正常的方法調用(含遞歸),這個深度徹底夠用,若是是創建多線程致使內存溢出,在不能減小線程數狀況下,只能經過減小最大堆和減小棧容量來換取更多的線程。
方法區以及運行時常量池溢出
因爲運行時常量池是方法區一部分,因此就放在一塊兒了。
下面上代碼:
1 public class RuntimeConstantPoolOOM{ 2 public static void main(String[] args) { 3 List<String> list = new ArrayList<String>(); 4 int i =0; 5 while(1) 6 list.add(String.valueOf(i++).intern()); 7 } 8 }
設置VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M。String.intern()是一個Native方法,它的做用是:若是字符串常量池已經包含了等於此String對象的字符串,則返回表明池中這個字符串的String對象:不然,將此String包含的字符串添加到常量池中,而且返回此String的引用。從結果中能夠看到,在OutOfMemoryError後跟隨的提示信息是:PermGen Space,說明運行時常量池屬於方法區一部分。
解決思路:
方法區溢出是一種常見的內存溢出異常,一個類要被垃圾收集器回收掉,判斷條件是比較苛刻的。在常常動態生成大量的Class應用中,須要特別注意類的回收情況,常見的場景有:大量jsp或動態生成jsp文件的應用、基於OSGI的應用以及使用GGLib字節碼加強(當前許多主流框架Spring、Hibernate都會用到GGLib這類字節碼技術)和動態語言。
本機直接內存溢出
DirectMemory容量能夠經過-XX:MaxDirectMemorySize指定,如不指定,默認與java堆最大值同樣。
下面貼代碼:
1 public class DirectMemroyOOM{ 2 3 public static final int _1MB = 1024 * 1024; 4 5 public static void main(String[] args) throws Exception{ 6 Field unsafeField = Unsafe.class.getDeclareFields()[0]; 7 unsafeField.setAccessible(true); 8 Unsafe unsafe = (Unsafe) unsafeField.get(null); 9 while (1) 10 unsafe.allocateMemory(_1MB); 11 } 12 }
指定VM Args: -Xmx20M -XX:MaxDirectMemorySize=10M,代碼越過了DirectByBuffer類,直接經過反射獲取Unsafe實例進行內存分配,之因此這麼作是由於雖然使用DirectByteBuffer分配內存也會拋出內存溢出異常,可是拋出的異常並無真正向操做系統申請分配內存,而是經過計算得知內存沒法分配,因而手動拋出異常,真正申請內存的方法是unsafe.allocateMemory()。
解決思路:
因爲此種內存溢出一個明顯的特徵是在Heap Dump文件中不會看見明顯的異常,若是發現OOM以後Dump文件很小,而程序中又直接或間接使用了NIO,那就能夠考慮一下是否是本機內存直接溢出了。
以上就是有關java內存溢出相關的總結了,若是有不對的地方,歡迎指出,你們一塊兒交流。
PS:本博客歡迎轉發,但請註明博客地址及做者~
博客地址:http://www.cnblogs.com/voidy/
<。)#)))≦