java虛拟機體内部系結構包括class檔案、類裝載子系統、運作時資料區、之行引擎、本地方法調用結構,其中運作時資料區包括方法區、堆、java棧、程式計數器、本地方法棧等。具體結構如下圖所示(摘自inside java virtual machine):

1. class檔案
在java中,所有源檔案都編譯成二進制的位元組碼,然後由虛拟機裝載運作。一般這樣的位元組碼是以class檔案的形式存在。在運作時,由classloader類(system classloader or user-defined classloader)找到對應的class檔案,讀取其中的位元組碼,然後交由虛拟機解析運作。
在class檔案中,包含了定義一個類或接口的所有資訊,包括類名、通路權限、父類名、繼承的所有接口、所有字段、所有方法、方法中的代碼、屬性等資訊,并且每個class檔案的開頭還包含了魔術值和版本資訊,魔術值用以辨別目前的位元組碼是合法的位元組碼,版本表示生成目前位元組碼的編譯器版本,進而虛拟機獲知其版本而做特定處理,如果對于虛拟機不支援的位元組碼版本号拒絕加載。
在class檔案中,很多資訊都是以字元串的形式存放,比如對外部類成員或方法的引用,這些字元串資訊在連結的時候由虛拟機解析。每個java類,不管是包成員類還是内部類都會生成一個單獨的class檔案,因而class檔案是相對獨立的。詳細資訊參考class檔案格式。
2. 類裝載子系統
類裝載子系統負責查找class檔案,讀取位元組碼,做部分簡單的檢驗,如魔數是否正确,版本是否受支援,各種資料格式是否正确等。部分解析後的位元組碼資料存放到方法區中,最後建立位元組碼代表的類或接口的class執行個體。
在java中,類裝載系統是通過classloader來完成的。虛拟機規範中,定義了啟動類裝載器和用于定義類裝載器。在sun提供的虛拟機中,包括了啟動類裝載器、擴充類裝載器、系統類裝載器、使用者定義類裝載器。他們以父子鍊的方式組織在一起。除了啟動類裝載器,其他的裝載器都是classloader的子類。classloader定義了一些方法可以幫助使用者定義自己的類裝載器,如defineclass等。詳情參考java中的classloader。
如何解除安裝類資料?(第七章)
3. 運作時資料區
運作時資料區儲存了所有在運作時的資訊。包括方法區、java棧、堆、程式寄存器、本地方法棧等。其中方法區和堆隻在虛拟機中儲存一份執行個體,因而需要處理多線程的同步問題;java棧、程式寄存器是每個線程中有單獨的執行個體,因而對不同的線程,他們的資料是私有的。
3.1 方法區
方法區中儲存了讀取的位元組碼資訊(包括常量池,靜态方法和靜态成員資訊)、位元組碼代表的class類執行個體、一個指向加載它的classloader執行個體。
java程式可以有兩種方式來擷取某個類的class執行個體:
1. class.forname()方法
2. object.getclass()方法
通過class執行個體擷取和該類或接口相關的任何資訊。參考class類的定義。
(注:對有啟動classloader加載的類,class方法中的getclassloader方法傳回null)
為加快執行速度,可以在方法區中引入方法表機制,記錄能被外界調用的該類的執行個體方法,包括父類中繼承下來的方法。(第八章詳細介紹?)
方法區中根據類名搜尋類資訊,算法:散列、搜尋樹等。
3.2 java棧
虛拟機為每個線程生成一個java棧,因而對不同的線程,棧内的資料都是私有的。java棧由棧幀組成,java棧的操作隻有兩種,壓入棧幀和彈出棧幀。線程中每個方法的調用都會在java棧壓入一個棧幀;每次方法傳回(正常方法或抛異常傳回),該方法對應的棧幀都會從棧中彈出。
3.2.1 棧幀
棧幀由操作數棧、局部變量區和棧幀資料組成。由于java中的指令是基于棧而設計的,因而很多指令的預設操作數就是操作數棧中的資料。操作數棧用于儲存指令的操作數和指令操作後的結果。
局部變量區用于儲存目前方法的局部變量。
棧幀資料區則儲存目前棧幀的資訊,如指向目前類常量池的指針,用于操作數為常量池索引的指令;還有一些和特定虛拟機實作相關的資訊和調試資訊。
3.3 程式寄存器
每個線程在執行時都會儲存目前指令的下一條指令的位址,以控制程式的之行流程。
3.4 堆
堆儲存了程式在運作時的所有對象。在java中,所有的對象都是儲存在堆中的,而外部通過對象的引用來通路對象。由于java存在垃圾回收器,因而java對象可能被移動,以減少記憶體碎片。其中一種實作可以很好的解決移動對象而需要改變所有該對象的引用變量的技術,即将堆分為句柄池和對象池。對象池中的對象儲存了對象的真正内容,而句柄池中的項包含兩個指針,一個指向對象,一個指向類資料。一個對象引用就是指向句柄的之戰。這樣當需要移動對象時,隻要改變句柄池中指向對象的指針值即可。然而這種設計是以犧牲速度為代價的,因為這樣每次通路對象就要多經曆一次指針定位。
在某些垃圾回收器實作中,對象需要額外的資訊,如果引用計數的垃圾收集器,需要為每個對象記錄引用計數資訊;而對另外有些機制,則可能需要暫時儲存某些資料。這些額外的資料可以儲存在類中,也可以在記錄在其他地方。類似的還有同步機制中的資料和記錄是否已經調用過finalize方法的資訊。
在java中有指令用于在記憶體中配置設定對象,卻沒有顯式的指令來釋放記憶體中的對象。
3.5 本地方法棧
當java方法調用本地方法的時候,目前線程的程式寄存器是不确定的值。程式的執行也轉向本地方法。本地方法可以正常傳回,也可以抛出異常。抛出的異常會在調用該本地方法的指令中重新抛出。
4. 執行引擎
每個使用者線程(即不包括垃圾回收線程等)都有一個執行引擎執行個體,用以執行位元組碼指令。
5. 本地方法接口
java程式可以通過本地方法接口來調用本地方法。