Java虛擬機通過裝載、連接和初始化一個JAVA類型,使該類型可以被正在運行的JAVA程序所使用,其中,裝載就是把二進制形式的JAVA類型讀入JAVA虛擬機中;而連接就是把這種讀入虛擬機的二進制形式的類型數據合并到虛擬機的運行時狀態中去。
連接階段分為三個子步驟----驗證、準備和解析。
"驗證"步驟確保了JAVA類型數據格式正確并且適用于JAVA虛擬機使用。
"準備"步驟負責為該類型分配它所需的內存,比如為它的類變量分配內存。
"解析"步驟則負責把常量池中的符號引用轉換為直接引用。
虛擬機的實現可以推遲解析這一步,它可以在當運行中的程序真正使用某個符號引用時再去解析它(把該符號引用轉換為直接引用)。當驗證、準備和(可選的)解析步驟都完成了時,該類型就已經為初始化做好了準備。在初始化期間都將給變量賦以適當的初始值。
?
所有的JAVA虛擬機實現必須在每個類或接口首次主動使用時初始化。下面這六種情況符合主動使用的要求。
(1)當創建某個類的新實例時(或通過在字節碼中執行new指令;或者通過不明確的創建、反射、克隆或者反序列化)
(2)當調用某個類的靜態方法時(即在字節碼中執行invoke、static指令時)
(3)當使用某個類或接口的靜態字段,或者對該字段賦值時,用final修飾的靜態字段除外,它被初始化為一個編譯時的常量表達式。
(4)當調用JAVA API中的某些反射方法時,比如類CLASS中的方法或者java.lang.reflect包中的類方法。
(5)當初始化某個類的子類時(某個類初始化時,要求它的超累已經被初始化了)
(6)當虛擬機啟動時被表名為啟動類的類(即含有main()方法的那個類)
?
裝載
裝載階段由三個基本動作組成,要裝載一個類型,JAVA虛擬機必須:
(1)通過該類型的完全限定名,產生一個代表該類型的二進制數據流。
(2)解析這個二進制數據流為方法去內的內部數據結構。
(3)創建一個表示該類型的java.lang.Class類的實例。
驗證
當類型被裝載后,就準備進行連接了。連接過程的第一步是驗證---確認類型符合JAVA語言的語義,并且它不會危及虛擬機的完整性。檢查被裝載的類型是否有任何問題的整個過程都屬于驗證。
另一個很可能在裝載時進行的檢查是,確保除了Object之外的每一個類都有一個超類。在裝載時檢查的原因是當虛擬機裝載一個類時,它必須確保該類的所有超類都已經被裝載了。
在大部分虛擬機實現中,還有一種檢查往往發生在正式的驗證階段之后,那就是符號引用的驗證。在前面的章節中描述過,動態連接的過程包括通過保存在常量池中的符號引用查找被引用的類、接口、字段以及方法,把符號引用替換成直接引用。當虛擬機搜尋一個被符號引用的(類型、字段或方法)時,它必須首先確認該元素存在。
?
在正式的驗證階段需要完成的候選檢查在下面列出:
(1)檢查final的類不能擁有子類。
(2)檢查final的方法不能被覆蓋。
(3)確保在類型和超類型之間沒有不兼容的方法聲明(比如兩個方法擁有相同的名字,參數再數量、順序、類型上都相同,但是返回類型不同)這里超類需要在子類初始化前被初始化。
(4)檢查所有的常量池入口相互之間一致。
(5)檢查字節碼的完整性。
(6)檢查常量池中的所有的特殊字符串是否符合格式。
?
準備
在準備階段,JAVA虛擬機為類變量分配內存,設置默認初始值。但在到達初始化階段之前,類變量都沒有被初始化為真正的初始值。JAVA虛擬機實現可能也為一些數據結構分配內存,目的是提高運行程序的性能。這種數據結構的雷子如方法表,它包含指向類中每一個方法(包括從超類繼承的方法)的指針。
?
解析
類型經過了連接的前兩個階段---之后,它就可以進入解析階段。解析過程是在類型的常量池中尋找類、接口、字段和方法的符號引用,把這些符號引用替換成直接引用的過程。
?
初始化
一個類包含兩個步驟:
1、如果累存在直接超類的話,且直接超類還沒有被初始化,就先初始化直接超類。
2、如果累存在一個類初始化方法,就執行此方法。
當初始化一個類的直接超類的時候,也就是包含這兩個步驟。因此,第一個初始化的類永遠是Object,然后被主動使用的類的繼承樹上的所有類。超類總是在子類之前被初始化。
JAVA虛擬機必須確保初始化過程被正確的同步。如果多個線程需要初始化一個類,僅僅允許一個線程來執行初始化,其他的線程需要等待。當活動的線程完成了初始化過程之后,它必須通知其他的等待的線程。
?
卸載類型
虛擬機創建并初始化對象,使程序能使用對象,然后在對象變得不再被引用后可選地執行垃圾收集。同樣,虛擬機裝載、連接并初始化類,使程序能使用類,當程序不在引用它們的時候可選地卸載它們。
垃圾收集器必須從可觸及的myThread類的對象,通過它在方法去中的類型數據找到可觸及的CLASS實例。
從MyThread對象開始,垃圾收集器跟隨一個指向MyThread的類型數據的指針,它找到了:
一個指向堆中的MyThread的Class實例的引用。
一個指向MyThread的直接超接口Cloneable的類型數據的指針。
一個指向MyThread的直接超類Thread的類型數據的指針。
從Cloneable的類型數據開始,垃圾收集器找到了:
一個指向堆中Cloneable的Class實例的引用
從Thread的類型數據開始,垃圾收集器找到了:
一個指向堆中Thread的Class實例的引用。
一個指向Thread的直接超接口Runnable的類型數據的指針。
一個指向Thread的直接超類Object的類型數據的指針。
從Runnable的類型數據開始,垃圾收集器找到了:
一個指向堆中Runnable的Class實例的引用。
從Object的類型數據開始,垃圾收集器找到了:
一個指向堆中Object的Class實例的引用。
僅僅一個可觸及的MyThread的實例,垃圾收集器就可以觸及MyThread和它所有超類型的Class實例。
?
參考:《深入java虛擬機》