一、背景知識
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<UUID> list = new ArrayList<UUID>();
- 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<byte[]> byteList = new ArrayList<byte[]>();
- 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();
- }
- }
看看控制檯運行的效果: