Java的虛擬機內存模型

JVM是JavaVirtualMachine(Java虛擬機)的縮寫,JVM是一種用於計算設備的規範,它是一個虛構出來的計算機,是通過在實際的計算機上仿真模擬各種計算機功能來實現的。Java語言的一個非常重要的特點就是與平臺的無關性。而使用Java虛擬機是實現這一特點的關鍵。一般的高級語言如果要在不同的平臺上運行,至少需要編譯成不同的目標代碼。而引入Java語言虛擬機後,Java語言在不同平臺上運行時不需要重新編譯。Java語言使用Java虛擬機屏蔽了與具體平臺相關的信息,使得Java語言編譯程序只需生成在Java虛擬機上運行的目標代碼(字節碼),就可以在多種平臺上不加修改地運行。Java虛擬機在執行字節碼時,把字節碼解釋成具體平臺上的機器指令執行。這就是Java的能夠“一次編譯,到處運行”的原因。

 

堆:

堆在虛擬機啓動時就被創建,是用來存放對象的內存空間,幾乎所有的對象都被放在堆中,這是一塊線程共享的區域。

在規範中有這樣一段描述:

所有的對象實例以及數組都要在堆上分配,但是隨着JIT編譯器的發展和逃逸分析技術逐漸成熟,棧上分配/標量替
換優化技術將會導致一些微妙的發生,所有的對象都分配在堆上也漸漸變得不是那麼絕對了.

java堆是垃圾回收機制主要場所,所以堆還可以細分爲新生代,老年代,在細分的話就是新生代分爲Eden空間/From Survivor空間/To Survivor空間等。

根據java虛擬機規範,Java堆可以處於物理上不連接的但是邏輯上連續的內存空間,在實現是既可以是規定大小也可以是可擴展的,當前主流的虛擬機都是按照空間可擴展的來實現的(通過-Xmx和-Xms來控制)。如果在堆內存中沒有內存完成實例分配,並且堆也無法擴展時,就會拋出內存溢出異常。

 

虛擬機棧:

虛擬機棧描述的就是java方法執行的內存模型,線程私有,棧的深度不能大於虛擬機所允許的深度,會拋出StackOverflowError異常。如果虛擬機棧是動態擴展的,在擴展時會申請內存,如果無法申請到足夠的內存,會報OutOfMemoryError異常。

 棧幀  :

用於支持虛擬機進行方法調用和方法執行的數據結構。他是虛擬機運行時數據區中虛擬機棧中的棧元素。棧幀保存了 局部變量表、操作數棧、動態鏈接、方法返回地址等信息。

每一個方法從調用開始到結束的操作叫做方法入棧和出棧的過程。

代碼在編譯期間,就確定了棧幀中需要多大的局部變量表和多深的操作數棧,因此一個棧幀需要分配多少內存不會受到運行時變量數據影響,而僅僅取決於具體的虛擬機的實現。

局部變量表:是用於存儲方法參數和方法內定義的局部變量的存儲空間,可以存儲編譯期間可知的各種基本數據類型,對象引用類型和返回地址(指向一條字節碼指令的地址)。表的最小單位爲32位,稱做變量槽,如果存儲大於32位則會分配兩個連續的槽空間。

操作數棧:也被稱爲造作棧,是一個後入先出棧。jvm底層字節碼指令集是基於棧類型,所有的操作碼都是對操作數棧上的數據進行操作,對於每一個方法的調用,jvm都會建立一個操作數棧,以供計算使用。操作數棧的大小也是編譯的時候就已知的。操作數棧操作的是Java任意數據類型。

當一個方法剛剛開始執行時,其操作數棧是空的,隨着方法執行和字節碼指令的執行,會從局部變量表或對象實例的字段中複製常量或變量寫入到操作數棧,再隨着計算的進行將棧中元素出棧到局部變量表或者返回給方法調用者,也就是出棧/入棧操作。一個完整的方法執行期間往往包含多個這樣出棧/入棧的過程。

動態鏈接: 在類加載階段中解析階段會將符號引用轉爲直接引用,被稱爲靜態解析,另外一部分將在每一次運行時期轉化啊爲直接引用,被稱爲動態鏈接。 每個棧幀都包含了一個指向運行時常量池中該棧幀所屬的方法引用,這是爲了支持方法調用過程中的動態鏈接。

方法區:

方法區主要存儲類的元數據的,如虛擬機加載的類信息、編譯後的代碼等。jdk 1.8之前方法區實現是被稱爲一種永久代的區域,這部分區域使用JVM內存,但是jdk8之後便移除了永久代,使用元空間實現,不同之處在於元空間使用的不是jvm內存,而是使用的系統內存。

在移除永久代之後,字符串常量池被放在了堆中,可能是方便垃圾回收器收集,而運行時常量池被放到了元空間裏,運行時常量池內放着靜態變量,字節碼等重要信息,它在哪我們才能把他稱爲方法區。

本地方法棧:

本地方法棧和虛擬機棧類似,不同點就是 本地方法棧服務的對象是jvm執行的native方法,而虛擬機服務的是jvm 執行的java方法。至於啥事native方法,簡單的解釋就是java調用的非java方法。

程序計數器:

這是一塊很小的內存空間,是唯一一個java虛擬機規範中沒有規定內存溢出異常的區域,作用是標記當前所執行的線程所執行的行號,執行native方法時,程序計數器值爲空。在虛擬機裏工作原理就是通過改變這個計數器的值來確定要執行下一條的指令。我們知道JVM的多線程實現方式是通過CPU時間片輪轉(即線程輪流切換並分配處理器執行時間)算法來實現的。也就是說,某個線程在執行過程中可能會因爲時間片耗盡而被掛起,而另一個線程獲取到時間片開始執行。當被掛起的線程重新獲取到時間片的時候,它要想從被掛起的地方繼續執行,就必須知道它上次執行到哪個位置,在JVM中,通過程序計數器來記錄某個線程的字節碼執行位置。因此,程序計數器是具備線程隔離的特性,也就是說,每個線程工作時都有屬於自己的獨立計數器。

 

 

 

發佈了10 篇原創文章 · 獲贊 2 · 訪問量 1420
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章