一、背景知識
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等。
示例代碼:
package com.jvm;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* 內存泄漏
* @author feizi
* @time 2015-1-23上午8:42:53
*/
public class OOMTest {
public static void main(String[] args) {
List list = new ArrayList();
while(true){
list.add(UUID.randomUUID());
}
}
}
看看控制台的輸出結果,因為我這邊的JVM設置的參數內存足夠大,所以需要等待一定的時間,才能看到效果:
如果是用CMD命令行,就可以自己指定參數編譯運行了,這樣效果就更快一些:
通過下列命令運行程序,注意先要用javac命令將.java源文件編譯成.class類字節碼文件。
java -Xms10M -Xmx10M -XX:-UseGCOverheadLimit OOMTest
2、內存溢出
內存溢出是指當我們新建一個實力對象時,實例對象所需占用的內存空間大於堆的可用空間。
如果出現了內存溢出問題,這往往是程序本生需要的內存大於了我們給虛擬機配置的內存,這種情況下,我們可以采用調大-Xmx來解決這種問題。
示例代碼:
package com.jvm;
import java.util.ArrayList;
import java.util.List;
/**
* 內存溢出
* @author feizi
* @time 2015-1-23上午8:56:22
*/
public class OOMTest_1 {
public static void main(String args[]){
List byteList = new ArrayList();
byteList.add(new byte[1000 * 1024 * 1024]);
}
}
看看控制台的運行效果:
使用CMD命令行指定參數運行:
java -verbose:gc -Xmn10M -Xms20M -Xmx20M -XX:+PrintGC OOMTest_1
三、線程棧
棧(JVM Stack)存放主要是棧幀( 局部變量表, 操作數棧 , 動態鏈接 , 方法出口信息 )的地方。注意區分棧和棧幀:棧里包含棧幀。與線程棧相關的內存異常有兩個:a)、StackOverflowError(方法調用層次太深,內存不夠新建棧幀)
b)、OutOfMemoryError(線程太多,內存不夠新建線程)
1、java.lang.StackOverflowError
棧溢出拋出java.lang.StackOverflowError錯誤,出現此種情況是因為方法運行的時候,請求新建棧幀時,
棧所剩空間小於戰幀所需空間。
例如,通過遞歸調用方法,不停的產生棧幀,一直把棧空間堆滿,直到拋出異常 :
示例代碼:
package com.jvm;
/**
* 棧溢出
* @author feizi
* @time 2015-1-23上午9:13:11
*/
public class SOFTest {
public void stackOverFlowMethod(){
stackOverFlowMethod();
}
/**
* 通過遞歸調用方法,不停的產生棧幀,一直把棧空間堆滿,直到拋出異常 :
* @param args
*/
public static void main(String[] args) {
SOFTest sof = new SOFTest();
sof.stackOverFlowMethod();
}
}
看看控制台運行的效果: