Java的棧幀和動態(tài)鏈接是什么?
在 Java 的面試過程中,不可避免的一個面試題那就是 JVM ,而 JVM 的面試題中,有各種,比如在堆中會被問到的關于垃圾回收機制的相關問題,在棧中會被問到入棧以及出棧的過程,今天我們就來聊一下關于棧的相關問題,比如,棧幀和動態(tài)鏈接指的是什么?
JVM
JVM(Java Virtual Machine,Java虛擬機)是Java平臺的核心組成部分,它是一個可以執(zhí)行Java字節(jié)碼的虛擬計算機。JVM的主要職責是加載Java類文件,并且根據(jù)這些類文件中的定義來執(zhí)行相應的操作。
JVM(Java Virtual Machine,Java虛擬機)主要包含以下幾個組成部分:
類加載器(Class Loader):負責加載字節(jié)碼文件到內(nèi)存,將.class文件中的類信息加載到JVM中,以便JVM能夠識別和使用這些類。
運行時數(shù)據(jù)區(qū)(Runtime Data Area):JVM的核心內(nèi)存空間結構模型,主要包括以下子區(qū)域:
- 方法區(qū)(Method Area):用于存儲虛擬機加載的類信息、常量、靜態(tài)變量,以及即時編譯器編譯后的代碼等數(shù)據(jù)。
- 堆(Heap):存儲Java程序創(chuàng)建的類實例(對象引用)。這是所有線程共享的內(nèi)存區(qū)域,用于存放對象實例。
- Java棧(JVM Stacks):每個虛擬機線程都有一個私有的棧,用于存儲局部變量、方法參數(shù)以及方法調(diào)用的相關信息。每個方法執(zhí)行時,都會創(chuàng)建一個棧幀來存儲這些信息。
- 程序計數(shù)器(Program Counter Register):一塊較小的內(nèi)存空間,作為當前線程所執(zhí)行的字節(jié)碼的行號指示器。它記錄了線程執(zhí)行的當前位置。
- 本地方法棧(Native Method Stack):與Java棧非常相似,但用于支持native方法的執(zhí)行。當JVM調(diào)用native方法時,會在這個棧中創(chuàng)建棧幀。
執(zhí)行引擎(Execution Engine):對JVM指令進行解析,翻譯成機器碼,然后提交到操作系統(tǒng)中執(zhí)行。它負責讀取JVM指令并驅動其執(zhí)行。
本地庫接口(Native Interface):允許Java代碼與其他語言寫的代碼進行交互。它提供了Java調(diào)用其他語言的原生庫的能力,使得Java程序能夠使用其他語言的庫和函數(shù)。
本地方法庫(Native Method Library):實現(xiàn)了Java本地方法的具體功能,這些方法是使用其他語言(如C或C++)編寫的,并通過本地庫接口與Java代碼進行交互。
JVM中的棧幀
在Java虛擬機(JVM)中,棧幀(Stack Frame)是用于支持方法調(diào)用和執(zhí)行的數(shù)據(jù)結構,是方法執(zhí)行時的內(nèi)存模型。每個方法從調(diào)用直至執(zhí)行完成的過程,都對應著一個棧幀在虛擬機棧中入棧到出棧的過程。
棧幀存儲了方法的局部變量表、操作數(shù)棧、動態(tài)鏈接、方法出口等信息。當一個方法被調(diào)用時,一個新的棧幀就會被創(chuàng)建并壓入到虛擬機棧中,這個棧幀中保存了方法的局部變量、實際參數(shù)、操作數(shù)棧、常量池引用等信息。當方法執(zhí)行完畢后,這個棧幀就會從虛擬機棧中彈出,接著執(zhí)行上一個方法棧幀中的操作。
棧幀的結構主要包括以下幾個部分:
局部變量表(Local Variable Table):存放了編譯期可知的各種基本數(shù)據(jù)類型(boolean、byte、char、short、int、float、long、double)、對象引用(reference類型,它不等同于對象本身,可能是一個指向對象起始地址的引用指針,也可能是指向一個代表對象的句柄或其他與此對象相關的位置)和returnAddress類型(指向了一條字節(jié)碼指令的地址)。
操作數(shù)棧(Operand Stack):也稱為表達式棧,主要用于保存計算過程的中間結果,同時作為計算過程中變量臨時的存儲空間。
動態(tài)鏈接(Dynamic Linking):指向運行時常量池的方法引用,使得方法中的符號引用在運行時可以直接定位到引用的目標,比如某個類的成員或者方法。
方法返回地址(Return Address):存放著調(diào)用該方法的PC寄存器的值。當一個方法執(zhí)行完畢后,會依賴這個方法出口來恢復上層方法的執(zhí)行。
圖片
就像上圖這樣,但是看圖的時候,又會有人發(fā)出疑問,既然動態(tài)鏈接都屬于棧幀了,那么為什么還會再標題上把他區(qū)分出來,我們就來說一下這個動態(tài)鏈接的問題。
棧幀當中的動態(tài)鏈接
動態(tài)鏈接是為了支持動態(tài)方法的調(diào)用過程,這句話看起來好像也沒什么毛病,但是總感覺很空,對著面試官如果說這句,那肯定還有下文,所以我們換成我們能理解的方式來解讀一下。
動態(tài)鏈接實際上就是符號引用轉變?yōu)橹苯右谩?/p>
符號引用轉為直接引用是類加載過程中的一個關鍵步驟,它發(fā)生在解析階段。符號引用是編譯原理中的概念,可以包括類和接口的全限定名、字段的名稱和描述符、方法的名稱和描述符等。
這些符號引用在Java字節(jié)碼中以CONSTANT_Class_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等類型的常量來表示。
而直接引用則是與內(nèi)存布局相關的,比如直接指向目標代碼的指針、相對偏移量或者是一個能間接定位到目標的句柄。直接引用是與虛擬機實現(xiàn)的內(nèi)存布局緊密相關的,同一個符號引用在不同虛擬機實例上甚至在同一虛擬機實例的不同類加載過程中可能都會轉換為不同的直接引用。
在類加載的解析階段,虛擬機將常量池內(nèi)的符號引用替換為直接引用的過程稱為解析。解析是類加載過程中必不可少的一個環(huán)節(jié)。如果符號引用無法進行解析,那么將會拋出一個異常,比如常見的java.lang.NoClassDefFoundError或java.lang.NoSuchFieldError、java.lang.NoSuchMethodError等。
解析動作主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調(diào)用點限定符7類符號引用進行。對于這7類符號引用,未必一定能在解析階段或第一次使用時就完成解析,有些符號引用是在真正使用的時候才進行解析的,這種解析方式稱為惰性解析。
總的來說,符號引用轉為直接引用是Java類加載過程中解析階段的一個重要步驟,它確保了符號引用能夠被正確地解析為內(nèi)存中的直接引用,從而實現(xiàn)Java程序的正常運行。
所以,你了解棧幀和動態(tài)鏈接了么?




























