JVM規範規定,除了程序計數器,虛擬機其餘內存區域均會發生內存溢出的可能,OutOfMemoryError(OOM) java
原文地址:http://www.begincode.net/blog/62 個人網站,歡迎你們多提意見 eclipse
本文目的: jvm
一、經過代碼人爲形成OOM,讓你們跟了解JVM運行時各區存儲的內容。 函數
二、經過demo讓你們實際開發過程當中,可以根據異常判斷是那個內存區域發生的溢出, 工具
三、讓你們瞭解到什麼樣的代碼會產生OOM,開發中可以儘可能規避。 測試
前提: 網站
先和你們介紹一下eclipse如何設置JVM參數,Xms 最小堆內存,Xmx最大堆內存, spa
HeapDumpOnOutOfMemoryError:發生內存溢出時生成堆轉儲快照 操作系統
1、JAVA堆內存溢出 .net
代碼實例,來自<<深刻理解JVM虛擬機>>
public class TestOOM { static class OOMObject{ } public static void main(String[] args) { List<OOMObject> list = new ArrayList<OOMObject>(); while(true){ list.add(new OOMObject()); } } }
運行結果以下,發生內存溢出,在OutOfMemoryError後面會跟着 Java heap space 提示是堆內存溢出,
同時生成 java_pid10308.hprof 文件
該文件能夠用分析工具MAT進行分析,安裝連接http://www.begincode.net/blog/45 也能夠用其餘工具,
java.lang.OutOfMemoryError: Java heap space Dumping heap to java_pid10308.hprof ... Heap dump file created [27883798 bytes in 0.497 secs]
經過工具能夠分析是內存泄露仍是內存溢出,若是不存在泄露,那就是存在着不少對象實例沒法回收,能夠經過分析查看是那些對象,是否必須存活,若是必須存活在考慮適當調整JVM堆參數,如Xmx Xms等在之後的文章中會在此介紹。
2、虛擬機棧和本地方棧溢出(棧溢出)
一、虛擬機棧
虛擬機棧溢出,理論上分爲兩種,一種是線程請求的棧深度大於虛擬機容許的最大深度,另一種是申請的空間不足。
由於虛擬機棧記錄的是局部變量(方法變量)和函數調用棧針,則產生堆棧溢出通常是函數調用深度,如遞歸類或者
方法A調用方法B,方法B調用方法C這樣調用深度超過了虛擬機容許的最大深度,
JVM參數設置爲以下,Xss128k Xss是堆棧大小
-Xms20m -Xmx20m -Xss128k -XX:+HeapDumpOnOutOfMemoryError
demo以下
public class TestOOM { static int stackIndex = 0; public void testStackOverFlow(){ stackIndex++; testStackOverFlow(); } public static void main(String[] args) throws Throwable { try{ TestOOM oom = new TestOOM(); oom.testStackOverFlow(); }catch(Throwable e){ System.out.println("堆棧深度(迭代次數):"+stackIndex); throw e; } } }
執行結果,堆棧深度達到983,堆棧溢出,並會給出具體是類,方法,發生的錯誤
堆棧深度(迭代次數):983 Exception in thread "main" java.lang.StackOverflowError at zgwx.TestOOM.testStackOverFlow(TestOOM.java:9) at zgwx.TestOOM.testStackOverFlow(TestOOM.java:10)
public void testStackOverFlow(){ stackIndex++; int str = 12; int str2 = 12; int str3 = 12; int str4 = 12; int str5 = 12; int str6 = 12; int str7 = 12; int str8 = 12; testStackOverFlow(); }
執行結果以下,迭代次數明顯減小,說明局部變量佔用了堆棧的空間
堆棧深度(迭代次數):579 Exception in thread "main" java.lang.StackOverflowError at zgwx.TestOOM.testStackOverFlow(TestOOM.java:9) at zgwx.TestOOM.testStackOverFlow(TestOOM.java:18)
二、本地方法棧,本地方法棧能夠經過建立線程的方式來實現溢出,由於java線程是映射到操做系統內核上,因此線程會調用本地方法,形成本地方法棧的溢出
本機環境沒測試出來,機器卡死了,你們能夠本身試試儘可能多建立線程,就會撐爆本地方法棧
下面是找來的例子,沒見到效果,都死卡死機了結
public class Test { private void runAlways(){ while(true){ } } public void createThread(){ while(true){ Thread thread = new Thread(new Runnable(){ public void run() { runAlways(); } }); thread.start(); } } public static void main(String[] args) { Test test = new Test(); test.createThread(); } }
3、直接本機內存溢出
本機內存溢出能夠無限制申請空間便可,用nio的申請緩衝區方式
public static void main(String[] args) { List list = new ArrayList(); while(true){ ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024*2048); list.add(byteBuffer); } }
執行結果以下,OutOfMemoryError後面的提示,Direct buffer memory 說明了直接緩衝區,
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory at java.nio.Bits.reserveMemory(Bits.java:658) at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
4、方法區(永久代)溢出
方法區存儲的是加載的類信息,常量,咱們就循環建立字符串常量,讓方法區溢出
爲了儘快溢出,jvm參數設置永久代參數5M: -XX:PermSize=5m -XX:MaxPermSize=5m
代碼以下:
public static void main(String[] args) { List list = new ArrayList(); int i = 0; while(true){ list.add((String.valueOf(i++)).intern()); } }
運行結果以下:OutOfMemoryError後面提示 PermGen space ;改代碼在jdk6及如下版本可以報出以下異常,jdk7高版本及以上部分jvm已經逐漸開始取消永久代的緣由,只會報出堆內存異常
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at com.TestSocket.main(TestSocket.java:12)