標籤:
概述
在說jvm記憶體劃分之前,先來說下java程式具體的執行流程:
Java源檔案經過java編譯器編譯後變成class位元組碼檔案,
Jvm的classloader載入class檔案完成後,交由execution engine執行。
執行引擎執行過程中用到的所有資料和資訊,都儲存在runtime data area中。
Runtime data area 就是我們常說的JVM記憶體。
Runtime data area
Runtime data area 都包括什麼呢?
《Java虛擬機器規範》中規定,運行時資料區,主要包含:本地方法棧(native method stack),java棧(VM stack),程式計數器(programs counter register),堆(heap),方法區(method area)。
《java虛擬機器規範》雖然規定了虛擬機器應該包含哪些部分,但是並沒有具體的實現規定,所以不同的虛擬機器會有不同的實現方式。
Runtime data area都存放哪些資料?
1,程式計數器
程式計數器:儲存的是下一條執行命令的記憶體位址。
在得到指令地址後,程式計數器會自動指向下一條指令的記憶體位址。具體實現視不同是虛擬機器而定,這裡將的只是概念和程式計數器的作用。
在JVM中,多線程是輪流擷取cpu執行權的,所以,每個線程都會有自己的程式計數器。並且他們不會互相干擾。
在JVM規範中,如果當前程式執行的是native方法,程式計數器中的值應該是下一條指令的地址,如果是非native方法,程式計數器中的值應該是undefined。
由於程式計數器中儲存的資料佔用的空間,不會隨程式的執行發生改變,所以程式計數器不會發生記憶體溢出現象。
2,java棧
Java棧也叫虛擬機器棧,也就是我們常說的棧,java棧是java方法執行的記憶體模型。
Java棧中存放的是一個個的棧幀,每個棧幀包括:局部變數表,運算元棧,運行時常量池的引用,方法返回地址,附加資訊。
當線程調用一個方法的時候,就會建立一個棧幀,並且進行壓棧操作。方法執行完畢後出棧。由此可知,線程當前執行的方法一定是在棧頂的。到這裡大家應該明白了,為什麼遞迴的時候,一個不注意就會棧溢出了。棧這部分空間對程式員而言是不透明的,完全由系統自動實施。
棧幀中的各部分都是什麼呢?
局部變數表
局部變數表:儲存方法中的局部變數,局部變數分基礎資料型別 (Elementary Data Type)和引用資料類型,
針對基礎資料型別 (Elementary Data Type)的變數,儲存的是值(每種基礎資料型別 (Elementary Data Type)都有固定的記憶體佔用大小),引用資料類型的變數儲存的是對象的引用。局部變數表的大小,在編譯器時期就可以確定,所以程式執行期間,局部變數表的大小是不會發生改變的。
運算元棧:線程執行方法的過程,實際上就是執行語句的過程,歸根到底就是電腦計算的過程。然而程式中所有的計算都是借住於運算元棧來完成的。可以理解成,它就是用來做計算的。
運行時常量池的引用:方法執行過程中可能會用到類中的常量,必須要有一個引用指向運行時常量。
方法返回地址:方法執行完成後,要返回之前調用它的地方,因此棧幀中必須儲存方法的返回地址。
附加資訊:java虛擬機器規範允許一些虛擬機器的具體實現增加一些規範中沒有描述的資訊到棧幀中,例如與調試相關的資訊,這部分資訊完全取決於虛擬機器的具體實現。
3,本地方法棧
本地方法棧和java棧的原理和作用是非常相似的。區別是值java棧是為執行java方法執行服務的。本地方法棧是執行為native方法服務的。JVM規範中並沒有對本地方發展的具體實現和資料介面做強制規定,虛擬機器的具體實現可以自由實現它。比如hotSopt虛擬機器中的實現就是將本地方法棧和java棧合二為一。
4,堆
Java中的堆是用來儲存物件本身和數組的(數組的指標在java棧中)。
堆這部分空間是java記憶體回收的主要區域。
堆是被所有線程共用的,在JVM中只有一個堆。
5,方法區
它和堆一樣是所有線程共用的。
方法區中主要儲存了每個類的資訊(比如類的名稱,方法資訊,欄位資訊),靜態變數,常量,編譯器編碼後的代碼等等。
常量池中除了包含代碼中所定義的各種基本類型(如int、long等等)和對象型(如String及數組)的常量值外,還包含一些以文本形式出現的符號引用(運行時常量池),比如:
類和介面的全限定名;
欄位的名稱和描述符;
方法和名稱和描述符。
補充:
在class檔案中除了類的 欄位,方法,介面等描述,還有一項資訊是常量池,用來儲存編譯期間產生的字面量和符號引用。
JVM記憶體劃分總結