對Java OutOfMemory異常的探究


Java堆溢出

虛擬機參數:

-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError

將堆的最小值和最大值都限制成為20M,-XX:+HeapDumpOnOutOfMemoryError出現內存異常時令java虛擬機Dump堆內存轉儲快照

代碼

 1 import java.util.*;
 2 
 3 /**
 4  * Created by zcy on 2017/6/11.
 5  */
 6 public class TestHeapMemory {
 7 
 8     static class OOMObject{
 9 
10     }
11 
12     public static void main(String[] args){
13         List<OOMObject> list = new ArrayList<OOMObject>();
14         while (true){
15             list.add(new OOMObject());
16         }
17     }
18 }

運行出現異常:

java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid9392.hprof ...
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3210)
    at java.util.Arrays.copyOf(Arrays.java:3181)
    at java.util.ArrayList.grow(ArrayList.java:261)
    at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:235)
    at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
Heap dump file created [28361944 bytes in 0.134 secs]
    at java.util.ArrayList.add(ArrayList.java:458)
    at TestHeapMemory.main(TestHeapMemory.java:15)

為了查看實時堆的使用情況,我們可以安裝VisualVM Launcher。

安裝請參考http://www.oschina.net/translate/setting-up-visualvm-in-under-5-minutes

使用VisualVM Launcher調試程序(需要在程序中sleep延時),發現堆內存一路上漲,最后崩掉了。

查看dump file,絕大多數內存都被數組占用了

要解決Heap的OutOfMemory,一般的手段是使用內存映像分析工具,分析堆轉儲文件,重點是確認內存中的對象是否是必要的,也就是要先分清楚到底是出現了內存泄露還是內存溢出。

如果是內存泄露,可以進一步使用工具查看到GC Roots的引用鏈。於是就能找到泄露對象是通過怎樣的路徑與GC Roots相關聯,導致GC無法自動回收。

如果每個對象都有必要存活着,那么應該檢查堆參數(-Xms和-Xmx)是否還可以調大

 

虛擬機棧和本地方法棧溢出

兩種異常:

  • StackOverFlow異常:線程請求的棧深度大於虛擬機允許的最大深度。
  • OutOfMemory異常:虛擬機擴展棧時無法申請到足夠的內存空間。

虛擬機參數:

-Xss128k

代碼:

 1 /**
 2  * Created by zcy on 2017/6/11.
 3  */
 4 public class TestStackOF {
 5 
 6     private static int stackLength = 0;
 7 
 8     public static void stackLeak() throws InterruptedException {
 9         stackLength++;
10         stackLeak();
11     }
12 
13     public static void main(String args[]) throws Throwable {
14         try{
15             TestStackOF.stackLeak();
16         }
17         catch (Throwable e){
18             System.out.println("stack length is: " + stackLength);
19             throw e;
20         }
21     }
22 }

運行結果:

stack length is: 1102
Exception in thread "main" java.lang.StackOverflowError
    at TestStackOF.stackLeak(TestStackOF.java:9)
    at TestStackOF.stackLeak(TestStackOF.java:10)
    at TestStackOF.stackLeak(TestStackOF.java:10)
    at TestStackOF.stackLeak(TestStackOF.java:10)
        ...

最后總結一下:

  • 對於StackOverFlow異常:棧深度(1000-2000)絕大多數情況下都夠用了。有錯誤堆棧可以閱讀。
  • 如果由於建立線程過多導致的內存溢出,可以減少堆容量和每個進程的棧容量換取更多的線程。

 

方法區和運行時常量池溢出

 


免責聲明!

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



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