Java虛擬機器3:記憶體溢出

來源:互聯網
上載者:User

標籤:pdu   valueof   logs   規範   利用   flow   技術分享   exce   article   

1.前言

上一篇我們介紹了java的記憶體地區結構,這一篇,類比記憶體溢出的幾個情境,下面一個圖是總體的指導思想:

2.Java堆溢出

Java堆唯一的作用就是儲存物件執行個體,只要保證不斷建立對象並且對象不被回收,那麼對象數量達到最大堆容量限制後就會產生記憶體溢出異常了。所以測試的時候把堆的大小固定住並且讓堆不可擴充即可。測試代碼如下:

 1 package com.xrq.test; 2  3 import java.util.ArrayList; 4 import java.util.List; 5  6 /** 7  * 測試內容:堆溢出 8  * 9  * 虛擬機器參數:-Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError10  */11 public class HeapOverflowTest12 {13     public static void main(String[] args)14     {15         List<HeapOverflowTest> list = new ArrayList<HeapOverflowTest>();16         while (true)17         {18             list.add(new HeapOverflowTest());19         }20     }21 }

運行結果

java.lang.OutOfMemoryError: Java heap spaceDumping heap to java_pid8876.hprof ...Heap dump file created [15782068 bytes in 0.217 secs]Exception in thread "main" java.lang.OutOfMemoryError: Java heap space    at java.util.Arrays.copyOf(Arrays.java:2760)    at java.util.Arrays.copyOf(Arrays.java:2734)    at java.util.ArrayList.ensureCapacity(ArrayList.java:167)    at java.util.ArrayList.add(ArrayList.java:351)    at com.xrq.test.HeapOverflowTest.main(HeapOverflowTest.java:18)

這種異常很常見,也很好發現,因為都提示了“Java heap space”了,定位問題的話,根據異常堆棧分析就好了,行號都有指示。

解決方案:可以調大堆的大小或者從代碼上檢視是否存在某些對象生命週期過長、持有狀態時間過長的情況,長時間減少程式運行期間的記憶體消耗。

另外,由於Java堆內也可能發生記憶體泄露(Memory Leak),這裡簡要說明一下記憶體泄露和記憶體溢出的區別:

    記憶體泄露:是指分配出去的記憶體沒有被回收回來,由於失去了對該記憶體地區的控制,因而造成了資源的浪費。Java中一般不會產生記憶體泄露,因為有記憶體回收行程自動回收垃圾,但這也不絕對,當我們new了對象,並儲存了其引用,但是後面一直沒用它,而記憶體回收行程又不會去回收它,這邊會造成記憶體泄露,

    記憶體溢出:是指程式所需要的記憶體超出了系統所能分配的記憶體(包括動態擴充)的上限。

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

運行時常量池也是方法區的一部分,所以這兩個地區一起看就可以了。這個地區的OutOfMemoryError可以利用String.intern()方法來產生。這是一個Native方法,意思是如果常量池中有一個String對象的字串就返回池中的這個字串的String對象;否則,將此String對象包含的字串添加到常量池中去,並且返回此String對象的引用。測試代碼如下:

 1 package com.xrq.test; 2  3 import java.util.ArrayList; 4 import java.util.List; 5  6 /** 7  * 測試內容:常量池溢出(這個例子也可以說明運行時常量池為方法區的一部分) 8  *  9  * 虛擬機器參數-XX:PermSize=10M -XX:MaxPermSize=10M10  */11 public class ConstantPoolOverflowTest12 {13     public static void main(String[] args)14     {15         List<String> list = new ArrayList<String>();16         int i = 0;17         while (true)18         {19             list.add(String.valueOf(i++).intern());20         }21     }22 }

運行結果

Exception in thread "Reference Handler" Exception in thread "main" java.lang.OutOfMemoryError: PermGen space    at java.lang.String.intern(Native Method)    at com.xrq.test.ConstantPoolOverflowTest.main(ConstantPoolOverflowTest.java:19)java.lang.OutOfMemoryError: PermGen space    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:123)

之前有講過,對於HotSpot而言,方法區=永久代,這裡看到OutOfMemoryError的地區是“PermGen space”,即永久代,那其實也就是方法區溢出了。注意一下JDK1.7下是不會有這個異常的,while迴圈將一直下去,因為JDK1.7之後溢出了永久代並採用Native Memory來實現方法區的規划了

4.棧溢出

Java虛擬機器規範中描述了如果線程請求的棧深度太深(換句話說方法調用的深度太深),就會產生棧溢出了。那麼,我們只要寫一個無限調用自己的方法,自然就會出現方法調用的深度太深的情境了。測試代碼如下

 1 package com.xrq.test; 2  3 /** 4  * 測試內容:棧溢出測試(遞迴調用導致棧深度不斷增加) 5  *  6  * 虛擬機器參數:-Xss128k 7  */ 8 public class StackOverflowTest 9 {10     private int stackLength = 1;11     12     public void stackLeak()13     {14         stackLength++;15         stackLeak();16     }17     18     public static void main(String[] args) throws Throwable19     {20         StackOverflowTest stackOverflow = new StackOverflowTest();21         try22         {23             stackOverflow.stackLeak();24         }25         catch (Throwable e)26         {27             System.out.println("stack length:" + stackOverflow.stackLength);28             throw e;29         }        30     }31 }

運行結果:

stack length:1006Exception in thread "main" java.lang.StackOverflowError    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:14)    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)    at com.xrq.test.StackOverflowTest.stackLeak(StackOverflowTest.java:15)
  ...

 

多線程:這裡需要注意一下多線程的情況,多線程下通過不斷建立線程的方式,可以產生OutOfMemoryError異常,因為每個線程都有自己的棧空間,棧空間越大,越容易產生記憶體溢出異常。其實這也很好理解,作業系統分配給進程的記憶體是有限制的,比如32位的Windows限制為2GB。虛擬機器提供了了參數來控制Java堆和方法區這兩部分記憶體的最大值,剩餘記憶體為2GB-最大堆容量-最大方法區容量,程式計數器很小就忽略了,虛擬機器進程本身的耗費也不算,剩下的記憶體就是棧的了。每個線程分配到的棧容量越大,可建立的線程數自然就越少,建立線程時就越容易把剩下的記憶體耗盡,可能造成作業系統的假死。

多線程導致的OutOfMemoryError,在不能減少線程數的情況下,就只能通過減少最大堆和每個線程的棧容量來換取更多的線程了。

單線程:StackOverFlowError這個異常,有錯誤堆棧可以閱讀,比較好定位。而且如果使用虛擬機器預設參數,棧深度在大多數情況下,達到1000~2000完全沒有問題,正常方法的調用這個深度應該是完全夠了。

轉載地址:http://www.cnblogs.com/xrq730/p/4833713.html

http://blog.csdn.net/ns_code/article/details/17565503

Java虛擬機器3:記憶體溢出

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.