Java虛擬機內存模型

jopen 11年前發布 | 26K 次閱讀 虛擬機 Java開發

1)程序計數器:線程私有

當線程數量超過CPU數量時,線程之間根據時間片輪詢搶奪CPU資源,對于單核CPU來說,每一個時刻,只能有一個線程在運行,而其他線程必須被切換出去。因此,每個線程都有一個獨立的程序計數器,用于記錄下一條要運行的指令,各個線程之間的計數器互不影響,是一塊線程的私有內存空間。當一個線程正在執行一個Java方法時,程序計數器記錄正在執行的Java字節碼地址,如果執行的是native方法,則計數器為空
2)Java虛擬機棧:線程私有

同Java線程同時間創建,用于保存方法的局部變量,部分結果,并參與方法的調用和返回。Java虛擬機定義了兩種與棧空間有關的異常:StackOverflowError,OutOfMemoryError。當請求的棧深度大于最大可用的棧深度,拋出StackOverflowError。如果棧可以動態擴展,擴展棧的過程中,沒有足夠的內存空間來支持棧的擴展(系統內存不足?),則拋出OutOfMemoryError。以下為一個拋出java.lang.StackOverflowError的實例

public class TestStack{
    private static int count=0;
    public static void recursion(long a,long b,long c){
        long d=0;
        long e=0;
        long f=0;
        count++;
        recursion(a,b,c);
    }
    public static void main(String []args){
        try{
            recursion(1L,2L,3L);
        }catch(Throwable e){
            System.out.println("count:"+count);
            System.out.print(e);
        }
    }
    public void recursion1(long a,long b,long c){
        long d=0;
        long e=0;
        long f=0;
        count++;
        recursion1(a,b,c);
    }
}
/*
直接運行
輸出:
    count:4927
使用-Xss來擴大棧空間,運行 java -Xss1m TestStack
輸出:
    count:27590
*/

虛擬機棧在運行時使用了一種叫做棧幀的數據結構保存上下文數據,在棧幀中,存放了方法的局部變量表、操作數棧、動態鏈接方法和返回地址等信息。每個方法的調用都伴隨著棧幀的入棧操作,方法的返回則表示棧幀的出棧操作。方法調用時,方法的參數和局部變量越多,局部變量表就越大,棧幀相應變大以滿足方法調用所需傳遞的信息。因此,單個方法調用所需的棧空間大小也會比較多
Java虛擬機內存模型
結論:使用-Xss來擴大棧空間,增加棧空間大小后,函數調用深度上升。函數的局部變量越多,棧幀就越大,單次函數調用對棧空間的需求也會增加,函數嵌套調用次數減少

局部變量表的空間是可重用的,對于一個方法所分配的最大局部變量表的容量,可以使用jclasslib工具來查看

public class Test1{

public static void main(String []args){
    test3();
}
public static void test1(){
    {
        byte[] b=new byte[6*1024*1024];
    }
    //此時b仍然在該棧幀的變量表中,
    //GC根可以引用到該內存塊,又未能有足夠多的局部變量來服用該內存塊,
    //因此不會被回收[1]
    System.gc();
    System.out.println("first explict gc over");        
}
public static void test2(){
    {
        byte[] b=new byte[6*1024*1024];
        b=null;//幫助系統GC
    }
    //被回收[2]
    System.gc();
    System.out.println("first explict gc over");    
}
public static void test3(){
    {
        byte[] b=new byte[6*1024*1024];
    }
    int a=0;//服用b的內存塊,GC根無法找到b,因此被回收[3]
    System.gc();
    System.out.println("first explict gc over");    
}

} /* java -verbose:gc Test1 --[1] [GC 224K->134K(5056K), 0.0027563 secs] [Full GC 134K->134K(5056K), 0.0177525 secs] [Full GC 6278K->6278K(11204K), 0.0151799 secs] first explict gc over

--[2] [GC 224K->134K(5056K), 0.0027134 secs] [Full GC 134K->134K(5056K), 0.0178466 secs] [Full GC 6278K->134K(11204K), 0.0152811 secs] first explict gc over

--[3] [GC 224K->134K(5056K), 0.0027211 secs] [Full GC 134K->134K(5056K), 0.0174787 secs] [Full GC 6278K->134K(11204K), 0.0147169 secs] first explict gc over */</pre>

3)本地方法棧:
與Java虛擬機棧功能相似,Java虛擬機棧用于管理Java函數調用,而本地方法棧用于管理本地方法的調用,有C實現。在Hot Spot虛擬機中不區分本地方法棧和Java虛擬機棧,因此,同樣也會拋出StackOverflowError,OutOfMemoryError異常

4)Java堆:Java運行時內存中最重要的部分
分配運行時所有的對象和數組。分為新生代(eden,survivor space0[s0,from space],survivor space1[s1,to space])和老年代。

5)方法區:線程共享
方法區存放的是類的類型信息,常量池,域信息,方法信息等大部分來自class文件的信息。在Hot Spot虛擬機中,方法區也叫做永久區,是一塊獨立于Java堆的內存空間,同樣也可以被GC回收,只是表現和Java堆不同

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
public class PermGenGC{
    public static void main(String []args){
        List<String> list=new ArrayList<String>();
        int i;
        for(i=0;i<Integer.MAX_VALUE;i++){
            //如果常量池中存在當前String,則返回池中對象,如果不存在,則先將String加入到常量池,再返回池中對象引用
            list.add(String.valueOf(i).intern());//加入到常量池
        }
    }
}
/*
java -XX:PermSize=2m -XX:MaxPermSize=4m PermGenGC

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at PermGenGC.main(PermGenGC.java:7)

java -XX:PermSize=2m -XX:MaxPermSize=4m -XX:+PrintGCDetails PermGenGC

[GC [DefNew: 896K->64K(960K), 0.0043064 secs] 896K->180K(5056K), 0.0050202 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC [DefNew: 919K->0K(960K), 0.0031302 secs] 1035K->236K(5056K), 0.0039759 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] [GC [DefNew: 778K->0K(960K), 0.0029668 secs] 1015K->390K(5056K), 0.0036493 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC [DefNew: 896K->0K(960K), 0.0030697 secs] 1286K->619K(5056K), 0.0037499 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC [DefNew: 896K->0K(960K), 0.0033067 secs] 1515K->964K(5056K), 0.0040117 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [Full GC [Tenured: 964K->581K(4096K), 0.0595687 secs] 1665K->581K(5056K), [Perm: 4095K->4095K(4096K)], 0.0610942 secs] [Times: user=0.06 sys=0.00, real=0.06 secs] [Full GC [Tenured: 581K->581K(4096K), 0.0591349 secs] 581K->581K(5056K), [Perm : 4095K->4095K(4096K)], 0.0636965 secs] [Times: user=0.06 sys=0.01, real=0.06 secs] Exception in thread "main" [Full GC [Tenured: 581K->236K(4096K), 0.0289138 secs] 596K->236K(5056K), [Perm : 4095K->360K(4096K)], 0.0332241 secs] [Times: user=0.03 sys=0.00, real=0.03 secs] java.lang.OutOfMemoryError: PermGen space at java.lang.String.intern(Native Method) at PermGenGC.main(PermGenGC.java:8) */</pre>來自:http://my.oschina.net/aptx4869/blog/323369

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!