1. 程式人生 > >jvm之記憶體結構詳解

jvm之記憶體結構詳解

       JVM的記憶體結構和各個記憶體區域的作用,對於理解Java記憶體機制、工作原理有著較大幫助。首先看一下《深入理解Java虛擬機器(第二版)》給出的JVM記憶體結構圖:

                                        

1.JVM結構分析

程式計數器:

當執行一條指令時,首先需要根據程式計數器(PC)中存放的指令地址,將指令由記憶體取到指令暫存器中,此過程稱為“取指令”。與此同時,PC中的地址或自動加1或由轉移指標給出下一條指令的地址。此後經過分析指令,執行指令。完成第一條指令的執行,而後根據PC取出第二條指令的地址,如此迴圈,執行每一條指令。

       程式計數器是一塊較小的記憶體空間,它的作用是當前執行緒所執行的位元組碼的行號指示器。在虛擬機器的概念模型裡(僅是概念模型,各種虛擬機器可能會通過一些更高效的方式去實現),位元組碼直譯器工作時就是通過改變這個計數器的值來選取下一條需要執行的位元組碼指令,分支、迴圈、跳轉、異常處理、執行緒恢復等基礎功能都需要依賴這個計數器來完成。每個執行緒都擁有一個屬於本執行緒的PC暫存器,當執行緒執行某個java方法時,PC暫存器的內容總是下一條被執行指令的”地址”,這裡的”地址”可以是一個本地指標,也可以是在方法位元組碼中相對於該方法起始指令的偏移量。如果該執行緒正在執行一個本地方法,那麼PC暫存器的值是”Undefined”。 

方法區:

       方法區(Method Area)是用於儲存類結構資訊的地方,包括常量池、靜態變數、建構函式等型別資訊,型別資訊是由類載入器在類載入時從類檔案中提取出來的。方法區中的常量池,包含著一些常量和符號引用(載入類的連線階段中的解析過程會將符號引用轉換為直接引用),方法區是執行緒共享的。

堆:

堆(heap)是儲存java例項或者物件的地方,是GC的主要區域,同樣是執行緒共享的記憶體區域。堆的優勢是可以動態地分配記憶體大小,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不在實用的資料。堆的缺點是由於執行時要動態分配記憶體,存取速度慢。

虛擬機器棧:

虛擬機器棧(Java Virtual Machine Stacks)和執行緒是緊密聯絡的,每建立一個執行緒時就會對應建立一個Java棧。其生命週期和執行緒相同,其由一系列幀組成,每個幀儲存一個方法的區域性變數、運算元棧、動態連結和返回地址。當我們每呼叫一個方法,都會建立一個棧幀,並壓棧。每一個方法從呼叫到最終返回結果的過程,就對應一個棧幀從入棧到出棧的過程。棧的優勢是存取速度比堆要快。棧的缺點是存在棧的資料大小和生存期必須是確定的,缺乏靈活性。

本地方法棧:

本地方法棧和虛擬機器棧的作用相似,不過虛擬機器棧是為使用者服務的,而本地方法棧是為Java底層(即jvm本身,jvm呼叫的native方法)方法服務的。

       這裡對虛擬機器棧和本地方法棧的區別做進一步說明:本地方法棧(Native Method Stacks)與虛擬機器棧所發揮的作用是非常相似的,其區別不過是虛擬機器棧為虛擬機器執行Java方法(也就是位元組碼)服務,而本地方法棧則是為虛擬機器使用到的Native方法服務。虛擬機器規範中對本地方法棧中的方法使用的語言、使用方式與資料結構並沒有強制規定,因此具體的虛擬機器可以自由實現它。甚至有的虛擬機器(譬如Sun HotSpot虛擬機器)直接就把本地方法棧和虛擬機器棧合二為一。與虛擬機器棧一樣,本地方法棧區域也會丟擲StackOverflowError和OutOfMemoryError異常。我們在進行理解時,同意記為棧即可。

2.結合程式碼作進一步說明

首先看程式碼:

public class MemoryStructureTest { // 執行時jvm會把該類的程式碼放入方法區
	
	public static String str = "abc";// 此靜態變數存於方法區的常量池
	
	public static void main(String[] args) {
		String string = new String("abc");// “string”即變數名放入棧,"abc"即值放入堆
		System.out.println(string);
	}
}

具體記憶體分配請看程式碼註釋。關於jvm為什麼要這樣分配,讀者在閱讀第一部分的關於堆疊的優勢和劣勢後,結果可想而知了。

特殊說明:

      基本型別,即定義是通過諸如int a = 3; long b = 255L;的形式來定義的,稱為自動變數。自動變數存的是字面值,不是類的例項,即不是類的引用,這裡並沒有類的存在 。如int a = 3; 這裡的a是一個指向int型別的引用,指向3這個字面值。這些字面值的資料,由於大小可知,生存期可知(這些字面值固定定義在某個程式塊裡面,程式塊退出後,欄位值就消失了),出於追求速度的原因,就直接存在於棧中。