首頁  >  文章  >  Java  >  Java虛擬機器(JVM)的知識點介紹

Java虛擬機器(JVM)的知識點介紹

WBOY
WBOY轉載
2023-04-22 23:40:061447瀏覽

一、什麼是Java虛擬機
當你談到Java虛擬機時,你可能是指:
1、抽象的Java虛擬機規格
2、一個具體的Java虛擬機實現
3、一個運作的Java虛擬機器實例
二、Java虛擬機器的生命週期
一個運作中的Java虛擬機器有著一個清晰的任務:執行Java程式。程式開始執行時他才運行,程式結束時他就停止。你在同一台機器上執行三個程序,就會有三個運作中的Java虛擬機。
Java虛擬機器總是開始於一個main()方法,這個方法必須是公有、回傳void、直接受一個字串陣列。在程式執行時,你必須給Java虛擬機器指明這個套件換main()方法的類別名稱。
Main()方法是程式的起點,他被執行的執行緒初始化為程式的初始執行緒。程式中其他的線程都由他來啟動。 Java中的線程分為兩種:守護線程 (daemon)和普通線程(non-daemon)。守護線程是Java虛擬機自己使用的線程,例如負責垃圾收集的線程就是一個守護線程。當然,你也可 以把自己的程式設定為守護線程。包含Main()方法的初始線程不是守護線程。
只要Java虛擬機器中還有普通的執行緒在執行,Java虛擬機器就不會停止。如果有足夠的權限,你可以呼叫exit()方法終止程式。
三、Java虛擬機器的架構
在Java虛擬機器的規格中定義了一系列的子系統、記憶體區域、資料型別和使用指南。這些元件構成了Java虛擬機器的內部結構,他們不僅為Java虛擬機器的實作提供了清晰的內部結構,更嚴格地規定了Java虛擬機器實現的外部行為。
每一個Java虛擬機器都由一個類別載入器子系統(class loader subsystem),負責載入程式中的類型(類別和介面),並賦予唯一的名字。每個Java虛擬機器都有一個執行引擎(execution engine)負責執行被載入類別中包含的指令。
程式的執行需要一定的記憶體空間,如字節碼、被載入類別的其他額外資訊、程式中的物件、方法的參數、傳回值、本地變數、處理的中間變數等等。 Java虛擬機器將 這些資訊統統保存在資料區(data areas)中。雖然每個Java虛擬機器的實作中都包含資料區,但是Java虛擬機器規格對資料區的規定卻非常的抽象。許多結構上的細節部分都留給了 Java虛擬機器實作者自己發揮。不同Java虛擬機器實作上的記憶體結構千差萬別。一部分實作可能佔用很多內存,而其他以下可能只佔用很少的內存;一些實現可 能會使用虛擬內存,而其他的則不使用。這種比較精煉的Java虛擬機器記憶體規則,可以讓Java虛擬機器可以在廣泛的平台上實現。
資料區中的一部分是整個程式共有,其他部分被單獨的執行緒控制。每個Java虛擬機器都包含方法區(method area)和堆疊(heap),他們都被整個程式共用。 Java虛擬機器載入並解析一個類別以後,將從類別檔案中解析出來的資訊保存與方法區中。程式執行時所建立的 物件都保存在堆中。
當一個執行緒被建立時,會被指派只屬於他自己的PC暫存器「pc register」(程式計數器)和Java堆疊(Java stack)。當執行緒不掉用本機方法時,PC暫存器中保存執行緒執行的下一條指令。 Java堆疊保存了一個執行緒呼叫方法時的狀態,包括本地變數、呼叫方法的 參數、回傳值、處理的中間變數。呼叫本地方法時的狀態保存在本地方法堆疊中(native method stacks),可能再寄存器或其他非平台獨立的記憶體中。
Java堆疊有堆疊區塊(stack frames (or frames))組成。堆疊塊包含Java方法呼叫的狀態。當一個執行緒呼叫一個方法時,Java虛擬機會將一個新的區塊壓到Java堆疊中,當這個方法運行結束時,Java虛擬機會將對應的區塊彈出並拋棄。
Java虛擬機器不使用暫存器保存計算的中間結果,而是用Java堆疊在存放中間結果。這是的Java虛擬機器的指令更緊湊,也更容易在一個沒有暫存器的裝置上實作Java虛擬機器。
圖中的Java堆疊中向下增長的,PC寄存器中線程三為灰色,是因為它正在執行本地方法,他的下一條執行指令不保存在PC寄存器中。
四、資料類型(Data Types)
所有Java虛擬機器中使用的資料都有確定的資料類型,資料類型和操作都在Java虛擬機器規格中嚴格定義。 Java中的資料型別分為原始資料型別 (primitive types)和引用資料型態(reference type)。引用類型依賴實際的對象,但不是對象本身。原始資料類型不依賴任何東西,他們就是本身表示的資料。
所有Java程式語言中的原始 資料類型,都是Java虛擬機器的原始資料類型,除了布林型(boolean)。當編譯器將Java原始碼編譯為自己碼時,使用整數(int)或位元組型 (byte)去表示布林型。在Java虛擬機器中使用整數0表示布林型的false,使用非零整數表示布林型的true,布林數組被表示為位元組數組,雖然他們可能會以位元組數組或位元組塊(bit fields)保存在堆中。
除了布林型,其他Java語言中的原始型別都是Java虛擬機器中的資料型別。在Java中資料型別被分成:整形的byte,short,int,long;char和浮點型的float,double。 Java語言中的資料類型在任何主機上都有相同的範圍。
在Java虛擬機器中也存在一個Java語言中不能使用的原始資料型別回傳值型別(returnValue)。這種類型被用來實現Java程式中的“finally clauses”,具體的參見18章的“Finally Clauses”。
引用類型可能被建立為:類別類型(class type),介面類型(interface type),陣列類型(array type)。他們都引用被動態創建的物件。當引用類型引用null時,說明沒有引用任何物件。
Java虛擬機器規格只定義了每種資料類型表示的範圍,沒有定義儲存時每種類型所佔用的空間。他們如何儲存由Java虛擬機器的實現者自己決定。關於浮點型更多資訊請參考14章「Floating Point Arithmetic」。

TypeRange
byte8-bit signed two's complement integer (-27 至 27 - 1, inclusive)
short16-bit signed two's complement integer (-215 to 215 - 1, inclusive-##int325bit signed two's complement integer (-231 to 231 - 1, inclusive)
long64-bit signed two's complement integer (-263 to 263 - 1, inclusive)
char16-bit unsigned Unicode to character inclusive)
float32-bit IEEE 754 single-precision float
double64-bit IEEE 754 double-precision float
returnValueaddress of an opcode within the same method# #reference to bereference to
五、位元組長度
Java虛擬機器中最小的資料單元式字(word),其大小由Java虛擬機器的實現者定義。但是一個字的大小必須足以容納byte,short,int, char,float,returnValue,reference;兩個字必須足夠容納long,double。所以虛擬機器的實現者至少提供的字不能小 於31bits的字,但是最好選擇特定平台上最有效率的字長。
在運作時,Java程式不能決定所執行機器的字長。字長也不會影響程式的行為,他只是在Java虛擬機器中的一種表現方式。
六、類別載入器子系統
Java虛擬機器中的類別載入器分為兩種:原始類別載入器(primordial class loader)和類別載入器物件(class loader objects)。原始類別載入器是Java虛擬機器實作的一部分,類別載入器物件是執行中的程式的一部分。不同類別載入器載入的類別被不同的命名空間所分割。
類別載入器呼叫了許多Java虛擬機器中其他的部分和java.lang套件中的許多類別。例如,類別載入物件就是java.lang.ClassLoader子類別的實例,ClassLoader類別中的方法可以存取虛擬機器中的類別載入機制;每一個被Java虛擬機器載入的類別都會被表示為一個java.lang.Class類別的實例。像其他物件一樣,類別載入器物件和Class物件都保存在堆中,被載入的資訊被保存在方法區中。
1、載入、連接、初始化(Loading, Linking and Initialization)
類別載入子系統不僅僅負責定位並載入類別文件,他按照以下嚴格的步驟作了很多其他的事情:(具體的信息請參閱第七章的「類別的生命週期」)
1)、載入:尋找並匯入指定類型(類別和介面)的二進位資訊
2)、連接:進行驗證、準備和解析
①驗證:確保導入類型的正確性
②準備:為類型分配記憶體並初始化為預設值
③解析:將字元引用解析為直接飲用
3)、初始化:呼叫Java程式碼,初始化類別變數為適當的值
2、原始類別載入器(The Primordial Class Loader)
每個Java虛擬機器都必須實作一個原始類別載入器,他能夠載入那些遵守類別檔案格式並且被信任的類。但是,Java虛擬機的規格並沒有定義如何載入類,這由 Java虛擬機實作者自己決定。對於給定類型名稱的類型,原始萊加載器必須找到那個類型名稱加“.class”的檔案並載入入虛擬機器中。
3、類別載入器物件
雖然類別載入器物件是Java程式的一部分,但是ClassLoader類別中的三個方法可以存取Java虛擬機器中的類別載入子系統。
1)、protected final Class defineClass(…):使用這個方法可以出入一個位元組數組,定義一個新的型別。
2)、protected Class findSystemClass(String name):載入指定的類,如果已經加載,就直接回傳。
3)、protected final void resolveClass(Class c):defineClass()方法只是載入一個類,這個方法負責後續的動態連接和初始化。
具體的訊息,請參閱第八章「連結模型」( The Linking Model)。
4、命名空間
當多個類別載入器載入了同一個類別時,為了保證他們名字的唯一性,需要在類別名稱前加上載入該類別的類別載入器的識別。具體的信息,請參閱第八章「連結模型」( The Linking Model)。
七、方法區(The Method Area)
在Java虛擬機器中,被載入類型的資訊都保存在方法區中。這寫資訊在記憶體中的組織形式由虛擬機器的實現者定義,例如,虛擬機器工作在一個「little- endian」的處理器上,他就可以將資訊保存為「little-endian」格式的,雖然在Java類別檔案中他們是以“big-endian”格式保存的。設計者可以用最適合併地機器的表示格式來儲存數據,以確保程式能夠以最快的速度執行。但是,在一個只有很小記憶體的裝置上,虛擬機器的實現者就不會佔用 很大的記憶體。
程式中的所有執行緒共用一個方法區,所以存取方法區資訊的方法必須是執行緒安全的。如果你有兩個線程都去載入一個叫Lava的類,那隻能由一個線程被容許去載入這個類,另一個必須等待。
在程式運行時,方法區的大小是可變的,程式在執行時可以擴展。有些Java虛擬機器的實作也可以透過參數也訂定方法區的初始大小,最小值和最大值。
方法區也可以被垃圾收集。因為程式中的內由類別載入器動態載入,所有類別可能變成沒有被引用(unreferenced)的狀態。當類變成這種狀態時,他就可 能被垃圾收集掉。沒有載入的類別包括兩種狀態,一種是真正的沒有加載,另一個是“unreferenced”的狀態。詳細資訊請參閱第七章的類別的生命週期 (The Lifetime of a Class)。
1、型別資訊(Type Information)
每一個被載入的型別,在Java虛擬機器中都會在方法區中儲存下列資訊:
1)、型別的全名(The fully qualified name of the type)
2)、類型的父類型的全名(除非沒有父型,或弗雷形式java.lang.Object)(The fully qualified name of the typeís direct superclass)
3)、給類型是一個類別還是介面(class or an interface)(Whether or not the type is a class )
4)、類型的修飾符(public,private,protected,static,final,volatile,transient等)( The typeís modifiers)
5)、所有父介面全名的清單(An ordered list of the fully qualified names of any direct superinterfaces)
類型全名保存的資料結構由虛擬機器實作者定義。除此之外,Java虛擬機器還要為每個類型保存以下資訊:
1)、類型的常數池(The constant pool for the type)
2)、類型欄位的資訊(Field information)
3)、類型方法的資訊(Method information)
4)、所有的靜態類別變數(非常量)資訊(All class (static) variables declared in the type, except constants)
5) 、一個指向類別載入器的參考(A reference to class ClassLoader)
6)、一個指向Class類別的參考(A reference to class Class)

1)、類型的常數池(The constant pool for the type)
常數池中保存中所有類型是用的有序的常數集合,包含直接常數(literals)如字串、整數、浮點數的常數,以及對型別、欄位、方法的符號參考。常數池 中每一個保存的常數都有一個索引,就像數組中的欄位一樣。因為常數池中保存中所有型別使用到的型別、欄位、方法的字元引用,所以它也是動態連結的主要對 象。詳細資訊請參閱第六章「The Java Class File」。
2)、類型欄位的資訊(Field information)
欄位名稱、欄位類型、欄位的修飾符(public,private,protected,static,final,volatile,transient等)、欄位在類別中定義的順序。
3)、型別方法的資訊(Method information)
方法名稱、方法的回傳值型別(或是void)、方法參數的數量、型別和他們的順序、欄位的修飾符(public, private,protected,static,final,volatile,transient等)、方法在類別中定義的順序
如果不是抽象和本地本法還需要保存
方法的字節碼、方法的操作數堆疊的大小和本地變數區的大小(稍候有詳細資訊)、異常列表(詳細資訊請參閱第十七章「Exceptions」。)
4)、類別(靜態)變數(Class Variables)
類別變數被所有類別的實例共享,即使不透過類別的實例也可以存取。這些變數綁定在類別上(而不是類別的實例上),所以他們是類別的邏輯資料的一部分。在Java虛擬機器使用這個類別之前就需要為類別變數(non-final)分配記憶體
常數(final)的處理方式於這種類別變數(non-final)不一樣。每一個類型在用到一個常數的時候,都會複製一份到自己的常數池中。常數也像類別變 量一樣保存在方法區中,只不過他保存在常數池中。 (可能是,類別變數被所有實例共享,而常數池是每個實例獨有的)。 Non-final類別變數保存為定義他的 類型資料(data for the type that declares them)的一部分,而final常數則保存為使用他的類型資料(data for any type that uses them)的一部分。詳情請參閱第六章「The Java Class FileThe Java Class File」
5)、指向類別載入器的參考(A reference to class ClassLoader)
每一個被Java虛擬機器載入的類型,虛擬機器必須保存這個類型是否由原始類別載入器或類別載入器載入。那些被類別載入器載入的型別必須保存一個指向類別載入器的引 用。當類別載入器動態連線時,會使用此資訊。當一個類別引用另一個類別時,虛擬機器必須保存那個被引用的類型是被同一個類別載入器載入的,這也是虛擬機器維護不同命 名空間的過程。詳情請參閱第八章「The Linking Model」
6)、指向Class類別的參考(A reference to class Class)
Java虛擬機器為每一個載入的型別建立一個java.lang.Class類別的實例。你也可以透過Class類別的方法:
public static Class forName(String className)來尋找或載入一個類,並取得對應的Class類的實例。透過這個Class類別的實例,我們可以存取Java虛擬機器方法區中的資訊。具體參考Class類別的JavaDoc。
2、方法清單(Method Tables)
為了更有效的存取所有保存在方法區中的數據,這些數據的儲存結構必須經過仔細的設計。在所有方法區中,除了保存了上邊的那些原始資訊外,還有一個為了加快存 取速度而設計的資料結構,例如方法清單。每一個被載入的非抽象類,Java虛擬機都會為他們產生一個方法列表,這個列表中保存了這個類別可能呼叫的所有實例 方法的引用,報錯那些父類中呼叫的方法。詳情請參閱第八章「The Linking Model」八、堆
當Java程式建立一個類別的實例或陣列時,都在堆中為新的物件分配記憶體。虛擬機器中只有一個堆,所有的執行緒都共用他。
1、垃圾收集(Garbage Collection)
垃圾收集是釋放沒有被引用的物件的主要方法。它也可能會為了減少堆的碎片,而移動物件。在Java虛擬機器的規格中沒有嚴格定義垃圾收集,只是定義一個Java虛擬機器的實作必須以某種方式管理自己的堆。詳情請參閱第九章「Garbage Collection」。
2、物件儲存結構(Object Representation)
Java虛擬機器的規格中沒有定義物件如何在堆中儲存。每一個物件主要儲存的是他的類別和父類別中定義的物件變數。對於給定的物件的引用,虛擬機器必須嫩耨很快的 定位到這個物件的資料。另為,必須提供一種透過物件的引用方法物件資料的方法,例如方法區中的物件的引用,所以一個物件保存的資料中往往含有一個某種形式 指向方法區的指標。
一個可能的堆的設計是將堆分為兩個部分:引用池和物件池。一個物件的引用就是指向引用池的本地指標。每一個引用池中的條目都包含兩個部分:指向物件池中對 象資料的指標和方法區中物件類別資料的指標。這種設計能夠方便Java虛擬機堆碎片的整理。當虛擬機器在物件池中移動一個物件的時候,只需要修改對應引用池中 的指標位址。但是每次訪問對象的資料都需要處理兩次指標。下圖示範了這種堆的設計。在第九章的“垃圾收集”中的HeapOfFish Applet演示了這種設計。
另一種堆的設計是:一個物件的參考就是一個指向一堆資料和指向對應物件的偏移指標。這種設計方便了物件的訪問,可是物件的移動要變的異常複雜。下圖示範了這種設計
當程式試圖將一個物件轉換為另一種類型時,虛擬機器需要判斷這種轉換是否是這個物件的類型,或者是他的父類型。當程式適用instanceof語句的時候也會做類似的事情。當程式呼叫一個物件的方法時,虛擬機器需要進行動態綁定,他必須判斷呼叫哪一個類型的方法。這也需要做上面的判斷。
無論虛擬機器實現者使用哪一種設計,他都可能為每個物件保存一個類似方法清單的資訊。因為他可以提升物件方法呼叫的速度,對提升虛擬機器的效能非常重要,但 是虛擬機器的規範中比沒有要求必須實現類似的資料結構。下圖描述了這種結構。圖中顯示了一個物件引用相關聯的所有的資料結構,包括:
1)、一個指向型別資料的指標
2)、一個物件的方法清單。方法列表是一個指向所有可能被呼叫物件方法的指標數組。方法資料包括三個部分:操作碼堆疊的大小和方法堆疊的本地變數區;方法的字節碼;異常列表。
每一個Java虛擬機器中的物件必須關聯一個用於同步多執行緒的lock(mutex)。同一時刻,只能有一個物件擁有這個物件的鎖。當一個擁有這個物件 的鎖,他就可以多次申請這個鎖,但是也必須釋放相應次數的鎖才能真正釋放這個物件鎖。很多物件在整個生命週期中都不會被鎖,所以這個資訊只有在需要時才需 要添加。許多Java虛擬機的實作都沒有在物件的資料中包含“鎖定資料”,但在需要時才產生對應的資料。除了實現物件的鎖定,每個物件也邏輯關聯到一 個「wait set」的實作。鎖定幫組執行緒獨立處理共享的數據,不需要妨礙其他的執行緒。 「wait set」幫組執行緒協作完成同一個目標。 「wait set」往往透過Object類別的wait()和notify()方法來實作。
垃圾收集也需要堆中的物件是否被關聯的資訊。 Java虛擬機規範中指出垃圾收集一個運行一個對象的finalizer方法一次,但是容許 finalizer方法重新引用這個對象,當這個對象再次不被引用時,就不需要再次調用finalize方法。所以虛擬機器也需要保存finalize方法 是否曾經運作過的資訊。更多資訊請參閱第九章的「垃圾收集」
3、陣列的保存(Array Representation)
在Java 中,陣列是一種完全意義上的對象,他和物件一樣保存在堆中、有一個指向Class類別實例的參考。所有相同維度和類型的陣列都有相同的Class,陣列的長 度不做考慮。對應Class的名字表示為維度和類型。例如一個整數資料的Class為“[I”,位元組型三維數組Class名為“[[[B”,兩維物件資料 Class名為“[[Ljava.lang.Object”。
多維數組被表示為數組的數組,如下圖:
數組必須在堆中保存數組的長度,數組的資料和一些物件數組類型資料的引用。透過一個陣列引用的,虛擬機器應該能夠取得一個陣列的長度,透過索引能夠存取特定 的數據,能夠呼叫Object定義的方法。 Object是所有資料類別的直接父類別。更多資訊請參閱第六章「類文件」。
九、PC暫存器(程式計數器)(The Program Counter)
每一個執行緒開始執行時都會被建立一個程式計數器。程式計數器只有一個字長(word),所以它能夠保存一個本機指標和returnValue。當執行緒執行 時,程式計數器中存放了正在執行指令的位址,這個位址可以使一個本地指針,也可以使一個從方法字節碼開始的偏移指針。如果執行本地方法,程式計數器的值沒 有定義。
十、Java堆疊(The Java Stack)
當一個執行緒啟動時,Java虛擬機會為他建立一個Java堆疊。 Java堆疊用一些離散的frame類別紀錄線程的狀態。 Java虛擬機堆Java堆疊的操作只有兩種:壓入和彈出frames。
在執行緒中正在執行的方法稱為當前方法(current method),當前方法所對應的frame稱為當前幀(current frame)。定義目前方法的類別稱為目前類別(current class),目前類別的常數池稱為目前常數池(current constant pool.)。當執行緒執行時,Java虛擬機會追蹤當前類別和當前常數池。但當線程操作保存在幀中的資料時,他只操作當前幀的資料。
當執行緒呼叫一個方法時,虛擬機會產生一個新的幀,並壓入執行緒的Java堆疊。這個新的幀變成當前幀。當方法執行時,他使用目前幀保存方法的參數、本地變 量、中間結構和其他資料。方法有兩種退出方式:正常退出和異常推出。無論方法以哪一種方式推出,Java虛擬機器都會彈出並丟棄方法的幀,上一個方法的幀變 為當前幀。
所有保存在幀中的資料都只能被擁有它的執行緒訪問,執行緒不能存取其他執行緒的堆疊中的資料。所以,存取方法的本地變數時,不需要考慮多執行緒同步。
和方法區、堆疊一樣,Java堆疊不需要連續的記憶體空間,它可以被保存在一個分散的記憶體空間或堆疊上。堆疊具體的資料和長度都有Java虛擬機器的實作者自己定義。一些實作可能提供了執行堆疊最大值和最小值的方法。
十一、堆疊幀(The Stack Frame)
堆疊幀包含三個部分:本地變數、操作數堆疊和幀資料。本地變數和操作數堆疊的大小都​​是一字(word)為單位的,他們在編譯就已經確定。幀資料的大小取決於 不同的實作。當程式呼叫一個方法時,虛擬機器會從類別資料中取得本地變數和操作數堆疊的大小,建立一個適當大小和幀,然後壓入Java堆疊中。
1、本地變數(Local Variables)
本地變數在Java堆疊訊框中被組織為一個從0計數的數組,指令透過提供他們的索引從本地變數區中取得對應的值。 Int,float,reference, returnValue佔一個字,byte,short,char被轉換成int然後存儲,long和doubel佔兩個字。
指令透過提供兩個字索引中的前一個來取得long,doubel的值。例如一個long的值儲存在索引3,4上,指令就可以透過3來取得這個long類型的值。
本地變數區中包含了方法的參數和本地變數。編譯器將方法的參數以他們申明的順序放在陣列的前面。但編譯器卻可以將本地變數任意排列在本地變數數組中,甚至兩個本地變數可以公用一個位址,例如,當兩個本地變數在兩個不交疊的區域內,就像循環變數i,j 。
虛擬機器的實作者可以使用任何結構來描述本地變數區中的數據,虛擬機器規格中沒有定義如何儲存long和doubel。
2、操作數堆疊(Operand Stack)
到本地變數一樣,操作數堆疊也被組織為一個以字為單位的陣列。但不像本地變數那樣透過索引訪問,而是透過push和pop值來實現訪問的。如果一個指令push一個值到堆疊中,那麼下一個指令就可以pop並且使用這個值。
操作數堆疊不像程式計數器那樣不可以被指令直接訪問,指令可以直接存取操作數堆疊。 Java虛擬機器是一個以堆疊為基礎,而不是以暫存器為基礎的,因為它的 指令從堆疊中取得操作數,而不是同一個暫存器。當然,指令也可以從其他地方去的操作數,例如指令後面的操作碼,或是常數池。但是Java虛擬機器指令主要是從 操作數堆疊中取得他們需要的操作數。
Java虛擬機將操作數堆疊視為工作區,許多指令透過先從操作數堆疊中pop值,在處理完以後再將結果push回操作數堆疊。一個add的指令執行過程如下圖所示:先執行iload_0和iload_1兩個指令將需要相加的兩個數,從本地方法區中取出,並push到操作數堆疊中;然後執行iadd指令,現pop出兩個值,相加,並將結果pusp進操作數堆疊中;最後執行istore_2指令,pop出結果,賦值到本地方法區。
3、幀資料(Frame Data)
處理本地變數和操作數堆疊以外,java堆疊幀還包括了為了支援常數池,方法返回值和異常分發所需的數據,他們被保存在幀數據中。
當虛擬機器遇到使用指向常數池引用的指令時,就會透過幀資料中指向常數區的指標來存取所需的資訊。前面提到過,常量區的引用在最開始時都是符號引用。即使當虛擬機器檢查這些引用時,他們也是字元引用。所以虛擬機器需要在這時轉換這個引用。
當一個方法正常回傳時,虛擬機器需要重建那個呼叫這個方法的方法的堆疊幀。如果執行完的方法有回傳值,虛擬機器就需要將這個值push進呼叫方法的哪個運算元堆疊中。
幀資料中也包含虛擬機器用來處理異常的異常表的參考。異常表定義了一個被catch語句保護的一段字節碼。每一個異常表中的個體又包含了需要保護的字節瑪的 範圍,和異常被捕捉到時需要執行的字節碼的位置。當一個方法拋出一個例外時,Java虛擬機器就是用異常表去判斷如何處理這個例外。如果虛擬機器找到了一個匹 配的catch,他就會將控制權交給catch語句。如果沒有找到匹配的catch,方法就會異常返回,然後再調用的方法中繼續這個過程。
除了以上的三個用途外,幀數據還可能包含一些依賴實現的數據,例如調試的資訊。
十二、本地方法堆疊
本地方法區依賴虛擬機器的不同實作。虛擬機器的實作者可以自己決定使用哪一種機制去執行本地方法。
任何本機方法介面(Native Method Interface)都使用某種形式的本機方法堆疊。
十三、執行引擎
一個java虛擬機器實現的核心就是執行引擎。在Java虛擬機規範,執行引擎被描述為一系列的指令。對於每一個指令,規範都描述了他們應該做什麼,但是沒有說要如何去做。
1、指令集
在Java虛擬機器中一個方法的字節碼流就是一個指令的序列。每一個指令由一個位元組的操作碼(Opcode)和可能存在的操作數(Operands)。操作 碼指示去做什麼,操作數提供一些執行這個操作碼可能需要的額外的資訊。一個抽象的執行引擎每次執行一個指令。這個過程發生在每一個執行的執行緒中。
有時,執行引擎可能會遇到一個需要呼叫本地方法的指令,在這種情況下,執行引擎會去試圖呼叫本地方法,但當本地方法返回時,執行引擎會繼續執行字節碼流中的下一個指令。本機方法也可以看成對Java虛擬機器中的指令集的一種擴充。
決定下一步執行那條指令也是執行引擎工作的一部分。執行引擎有三種方法可以取得下一條指令。多數指令會執行跟在他會面的指令;一些像goto, return的指令,會在他們執行的時候決定他們的下一條指令;當一個指令拋出異常時,執行引擎通過匹配catch語句來決定下一條應該執行的指令。
平台獨立性、網路移動性、安全性左右了Java虛擬機器指令集的設計。平台獨立性是指令集設計的主要影響因素之一。基於堆疊的結構使得Java虛擬機器可以在 更多的平台上實現。更小的操作碼,緊湊的結構使得字節碼可以更有效的利用網路頻寬。一次性的字節碼驗證,使得字節碼更安全,而不影響太多的效能。
2、執行技術
許多種執行技術可以用在Java虛擬機器的實作中:解釋執行,及時編譯(just-in-time compiling),hot-spot compiling,native execution in silicon。
3、執行緒
Java虛擬機器規格定義了一種為了在更多平台上實現的執行緒模型。 Java執行緒模型的一個目標時可以利用本機執行緒。利用本機執行緒可以讓Java程式中的執行緒能過在多處理器機器上真正的同時執行。
Java執行緒模型的一個代價就是執行緒優先權,一個Java執行緒可以在1-10的優先權上運行。 1最低,10最高。如果設計者使用了本地線程,他們可能會將這 10個優先權對應到本地優先權上。 Java虛擬機規格只定義了,高一點優先權的執行緒可以卻一些cpu時間,低優先權的執行緒在所有高優先權執行緒都堵塞時,也可以取得一些cpu時間,但這沒有保證:低優先權的執行緒在高優先權執行緒沒有堵塞時不可以獲得一定的cpu時間。因此,如果需要在不同的線程間協作,你必 須使用的「同步(synchronizatoin)」。
同步意味著兩個部分:物件鎖(object locking)和執行緒等待、啟動(thread wait and notify)。物件鎖幫助執行緒可以不受其他執行緒的干擾。執行緒等待、啟動可以讓不同的執行緒進行協作。
在Java虛擬機器的規格中,Java執行緒被描述為變數、主記憶體、工作記憶體。每一個Java虛擬機的實例都有一個主內存,他包含了所有程式的變數:物件、數字組合類別變數。每個線程都有自己的工作內存,他保存了哪些他可能用到的變數的拷貝。規則:
1)、從主記憶體拷貝變數的值到工作記憶體中
2)、將工作記憶體中的值寫入會主記憶體中
如果一個變數沒有被同步化,執行緒可能以任何順序更新主記憶體中的變數。為了保證多執行緒程式的正確的執行,必須使用同步機制。
十四、本機方法介面(Native Method Interface)
Java虛擬機器的實作並不是必須實作本機方法介面。一些實作可能根本不支援本地方法介面。 Sun的本機方法介面是JNI(Java Native Interface)。
十五、現實中的機器(The Real Machine)
十六、數學方法:模擬(Eternal Math : A Simulation)

以上是Java虛擬機器(JVM)的知識點介紹的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:yisu.com。如有侵權,請聯絡admin@php.cn刪除