Java內存溢出與棧溢出

一、背景知識


1、JVM體系結構




2、JVM運行時數據區




3、JVM內存模型


JVM運行時內存 = 共享內存區 + 線程內存區




3-1、共享內存區


共享內存區 = 持久帶 + 堆

持久帶 = 方法區 + 其他

堆 = Old Space + Young Space

Young Space = Eden + S0 + S1




3-1-1、持久代


JVM用持久帶(Permanent Space)實現方法區,主要存放所有已加載的類信息,方法信息,常量池等等。

可通過-XX:PermSize和-XX:MaxPermSize來指定持久帶初始化值和最大值

Permanent Space並不等同於方法區,只不過是Hotspot JVM用Permanent Space來實現方法區而已,有些虛擬機沒

有Permanent Space而用其他機制來實現方法區。


3-1-2、堆


堆(heap),主要用來存放類的對象實例信息(包括new操作實例化的對象和定義的數組)。

堆分爲Old Space(又名,Tenured Generation)和Young Space。

Old Space主要存放應用程序中生命週期長的存活對象;

Eden(伊甸園)主要存放新生的對象;

S0和S1是兩個大小相同的內存區域,主要存放每次垃圾回收後Eden存活的對象,作爲對象從Eden過渡到Old Space

的緩衝地帶(S是指英文單詞Survivor Space)。

堆之所以要劃分區間,是爲了方便對象創建和垃圾回收,後面垃圾回收部分會解釋。


3-2、線程內存區


線程內存區=單個線程內存+單個線程內存+.......

單個線程內存=PC Regster+JVM棧+本地方法棧

JVM棧=棧幀+棧幀+.....

棧幀=局域變量區+操作數區+幀數據區




在Java中,一個線程會對應一個JVM棧(JVM Stack),JVM棧裏記錄了線程的運行狀態。

JVM棧以棧幀爲單位組成,一個棧幀代表一個方法調用。棧幀由三部分組成:局部變量區、操作數棧、幀數據區。


二、堆溢出


堆(Heap)是Java存放對象實例的地方。

堆溢出可以分爲以下兩種情況,這兩種情況都會拋出OutOfMemoryError:java heap space異常:


1、內存泄漏


內存泄漏是指對象實例在新建和使用完畢後,仍然被引用,沒能被垃圾回收釋放,一直積累,直到沒有剩餘

內存可用。

如果內存泄露,我們要找出泄露的對象是怎麼被GC ROOT引用起來,然後通過引用鏈來具體分析泄露的原因。

分析內存泄漏的工具有:Jprofiler,visualvm等。


示例代碼:

  1. package com.jvm;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5. import java.util.UUID;  
  6.   
  7. /** 
  8.  * 內存泄漏 
  9.  * @author feizi 
  10.  * @time 2015-1-23上午8:42:53 
  11.  */  
  12. public class OOMTest {  
  13.   
  14.     public static void main(String[] args) {  
  15.           
  16.         List<UUID> list = new ArrayList<UUID>();  
  17.         while(true){  
  18.             list.add(UUID.randomUUID());  
  19.         }  
  20.     }  
  21.   
  22. }  



看看控制檯的輸出結果,因爲我這邊的JVM設置的參數內存足夠大,所以需要等待一定的時間,才能看到效果:






如果是用CMD命令行,就可以自己指定參數編譯運行了,這樣效果就更快一些:

通過下列命令運行程序,注意先要用javac命令將.java源文件編譯成.class類字節碼文件。


  1. java -Xms10M -Xmx10M -XX:-UseGCOverheadLimit OOMTest  


2、內存溢出


內存溢出是指當我們新建一個實力對象時,實例對象所需佔用的內存空間大於堆的可用空間。

如果出現了內存溢出問題,這往往是程序本生需要的內存大於了我們給虛擬機配置的內存,這種情況下,我們可以採用調大-Xmx來解決這種問題。


示例代碼:

  1. package com.jvm;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.List;  
  5.   
  6. /** 
  7.  * 內存溢出 
  8.  * @author feizi 
  9.  * @time 2015-1-23上午8:56:22 
  10.  */  
  11. public class OOMTest_1 {  
  12.     public static void main(String args[]){  
  13.         List<byte[]> byteList = new ArrayList<byte[]>();  
  14.         byteList.add(new byte[1000 * 1024 * 1024]);  
  15.     }  
  16. }  



看看控制檯的運行效果:




使用CMD命令行指定參數運行:


  1. java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest_1  




三、線程棧


棧(JVM Stack)存放主要是棧幀( 局部變量表, 操作數棧 , 動態鏈接 , 方法出口信息 )的地方。注意區分棧和棧幀:棧裏包含棧幀。

與線程棧相關的內存異常有兩個:

a)、StackOverflowError(方法調用層次太深,內存不夠新建棧幀)

b)、OutOfMemoryError(線程太多,內存不夠新建線程)


1、java.lang.StackOverflowError


棧溢出拋出java.lang.StackOverflowError錯誤,出現此種情況是因爲方法運行的時候,請求新建棧幀時,

棧所剩空間小於戰幀所需空間。

例如,通過遞歸調用方法,不停的產生棧幀,一直把棧空間堆滿,直到拋出異常 :


示例代碼:

  1. package com.jvm;  
  2. /** 
  3.  * 棧溢出 
  4.  * @author feizi 
  5.  * @time 2015-1-23上午9:13:11 
  6.  */  
  7. public class SOFTest {  
  8.   
  9.     public void stackOverFlowMethod(){  
  10.         stackOverFlowMethod();  
  11.     }  
  12.       
  13.     /** 
  14.      * 通過遞歸調用方法,不停的產生棧幀,一直把棧空間堆滿,直到拋出異常 : 
  15.      * @param args 
  16.      */  
  17.     public static void main(String[] args) {  
  18.         SOFTest sof = new SOFTest();  
  19.         sof.stackOverFlowMethod();  
  20.     }  
  21.   
  22. }  


看看控制檯運行的效果:




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章