面試準備之java虛擬機器記憶體結構

鍾離四郎發表於2018-07-26

##虛擬機器記憶體結構

虛擬機器記憶體結構.jpg

###程式計數器 執行緒私有的,學過ARM或者彙編的同學應該很熟悉,表示的是當前執行緒執行到了哪一行位元組碼。

java虛擬機器棧

“java虛擬機器棧”這個名字很讓人誤會,看起來一點都不像是執行緒私有的,看起來像是“虛擬機器”級別的東西,更像是執行緒共享的,但實際上它確實是執行緒私有的,所以我覺得它更應該叫做“執行緒棧”諸如此類更加“人如其名”的稱呼。“java虛擬機器棧”的生命週期是和執行緒相同的,執行緒執行某個方法的時候會為方法建立一個叫做“棧幀”的資料結構進行入棧,這個“棧幀”用來記錄方法的區域性變數等資訊。“java虛擬機器棧”深度是有限的,如果一個執行緒在呼叫方法數超過棧所能容納的深度時候會出現StackOverflowError,告訴你棧爆了,怎麼模擬這種情況呢,哈哈,執行沒有遞迴終止條件的遞迴方法,如下:

public class App {

	public static int f(int num) {
		// 把遞迴終止條件去掉
		// if(num>10){
		// System.out.println("----->"+num);
		// return num;
		// }
		num++;
		return f(num);

	}

	public static void main(String[] args) {
		f(0);
	}
}
複製程式碼

執行上述程式碼會出現StackOverflowError,如下:

面試準備之java虛擬機器記憶體結構

本地方法棧

又是一個棧,還是本地的(native),容易想到這是一個與本地方法有關的棧,實際上確實如此,本地方法棧與java虛擬機器棧發揮的作用是非常相似的,它們的區別在於虛擬機器棧是為java方法服務,本地方法棧為本地方法服務,所以本地方法棧也是執行緒私有的。

java堆

java堆是執行緒共享的用來儲存類例項和陣列物件的(但不是唯一不能夠類例項和陣列物件)儲存區域。java堆上的物件生命週期不一,有長有短,為了提高回收效率,採用了分代收集的策略。這裡的“代”可以分為“新生代”、“老年代”

java堆結構.png
上圖結構可以看到,新生代可以分為Eden、Survivor,Survivor還分為From、to兩部分,Survivor是存放年齡不大的物件,而Eden存放的是新生物件(PS:Eden是伊甸園的意思,亞當夏娃他們家,搞不懂為啥叫這名,這裡姑且記住行了,不糾結!),而oldGen則相對好理解了,存放的是生命週期比較長的物件。看到這裡你可能會有個疑問就是——“我建立一個物件怎麼變成老生代的物件?”這裡打個不太恰當的比方,如果一個人不要太短命,活過60歲被送敬老院,這時候我們說這個人是老年人了,而物件這個“身份”的轉變過程也是有點相似的,結合下文例子就更加容易理解了:
image.png
新生代存放了大多數新生物件(注意是大多數,大物件可能直接往老年代裡放,畢竟大物件回收再建立比較昂貴,傷不起!),From Survivor存放的是不至於老到變成老年代的物件,To Survivor則是還沒使用的空間。假設此時來一次Minor Gc(指的是針對新生代的回收),演示如下: s

Minor Gc.png
上圖中打叉的矩形表示被回收物件,(a)表示Minor Gc的過程,(b)表示Minor Gc的結果,Eden中存活的物件複製到未使用Survivor,在被佔用的Survivor裡不夠老的也複製到未使用的Survivor,而那些經過Minnor Gc 存活且足夠老的則加入老年代,之後Survivor兩部分(from、to)交換角色,而Eden則完全為空。

方法區

執行緒共享的用來儲存系統的類資訊(包括類的欄位、方法、常量池、靜態變數)的儲存區域,方法區也叫永久代,這個是因為它不像java堆一樣被Gc回收得那麼頻繁,但也不是說不能被回收,如果一個類不在被“引用”,這個類就應該從虛擬機器解除安裝,被解除安裝的類是可以被回收的,如果系統的類太多的話也會導致方法去溢位。

總結

image.png

相關文章