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
*/ 虛擬機棧在運行時使用了一種叫做棧幀的數據結構保存上下文數據,在棧幀中,存放了方法的局部變量表、操作數棧、動態鏈接方法和返回地址等信息。每個方法的調用都伴隨著棧幀的入棧操作,方法的返回則表示棧幀的出棧操作。方法調用時,方法的參數和局部變量越多,局部變量表就越大,棧幀相應變大以滿足方法調用所需傳遞的信息。因此,單個方法調用所需的棧空間大小也會比較多
結論:使用-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