① 類加載過程
從上面的圖片我們可以看出整個 JVM 執行的流程中,和程序員關系最密切的就是類加載的過程了,所以
接下來我們來看下類加載的執行流程。
對于一個類來說,它的生命周期是這樣的:
其中前 5 步是固定的順序并且也是類加載的過程,其中中間的 3 步我們都屬于連接,所以對于類加載來
說總共分為以下幾個步驟:
1. 加載
2. 連接
1. 驗證
2. 準備
3. 解析
3. 初始化
下面我們分別來看每個步驟的具體執行內容。
1) 加載
“加載”(Loading)階段是整個“類加載”(Class Loading)過程中的一個階段,它和類加載 Class
Loading 是不同的,一個是加載 Loading 另一個是類加載 Class Loading,所以不要把二者搞混了。
在加載 Loading 階段,Java虛擬機需要完成以下三件事情:
1)通過一個類的全限定名來獲取定義此類的二進制字節流。
2)將這個字節流所代表的靜態存儲結構轉化為方法區的運行時數據結構。
3)在內存中生成一個代表這個類的java.lang.Class對象,作為方法區這個類的各種數據的訪問入口。
2) 驗證
驗證是連接階段的第一步,這一階段的目的是確保Class文件的字節 流中包含的信息符合《Java虛擬機
規范》的全部約束要求,保證這些信 息被當作代碼運行后不會危害虛擬機自身的安全。
驗證選項:
文件格式驗證
字節碼驗證
符號引用驗證...
3) 準備
準備階段是正式為類中定義的變量(即靜態變量,被static修飾的變量)分配內存并設置類變量初始值
的階段。
比如此時有這樣一行代碼:
public static int value = 123;
它是初始化 value 的 int 值為 0,而非 123。
4) 解析
解析階段是 Java 虛擬機將常量池內的符號引用替換為直接引用的過程,也就是初始化常量的過程。
5) 初始化
初始化階段,Java 虛擬機真正開始執行類中編寫的 Java 程序代碼,將主導權移交給應用程序。初始化
階段就是執行類構造器方法的過程。
② 雙親委派模型
提到類加載機制,不得不提的一個概念就是“雙親委派模型”。
站在 Java 虛擬機的角度來看,只存在兩種不同的類加載器:一種是啟動類加載器(Bootstrap
ClassLoader),這個類加載器使用 C++ 語言實現,是虛擬機自身的一部分;另外一種就是其他所有的
類加載器,這些類加載器都由Java語言實現,獨立存在于虛擬機外部,并且全都繼承自抽象類
java.lang.ClassLoader。
站在 Java 開發人員的角度來看,類加載器就應當劃分得更細致一 些。自 JDK 1.2 以來,Java 一直保持
著三層類加載器、雙親委派的類加載架構器。
什么是雙親委派模型?
如果一個類加載器收到了類加載的請求,它首先不會自己去嘗試加載這個類,而是把這個請求委派給父
類加載器去完成,每一個層次的類加載器都是如此,因此所有的加載請求最 終都應該傳送到最頂層的啟
動類加載器中,只有當父加載器反饋自己無 法完成這個加載請求(它的搜索范圍中沒有找到所需的類)
時,子加載器才會嘗試自己去完成加載。
啟動類加載器:加載 JDK 中 lib 目錄中 Java 的核心類庫,即$JAVA_HOME/lib目錄。 擴展類加載
器。加載 lib/ext 目錄下的類。
應用程序類加載器:加載我們寫的應用程序。
自定義類加載器:根據自己的需求定制類加載器。
雙親委派模型的優點
1. 避免重復加載類:比如 A 類和 B 類都有一個父類 C 類,那么當 A 啟動時就會將 C 類加載起來,那
么在 B 類進行加載時就不需要在重復加載 C 類了。
2. 安全性:使用雙親委派模型也可以保證了 Java 的核心 API 不被篡改,如果沒有使用雙親委派模
型,而是每個類加載器加載自己的話就會出現一些問題,比如我們編寫一個稱為 java.lang.Object
類的話,那么程序運行的時候,系統就會出現多個不同的 Object 類,而有些 Object 類又是用戶
自己提供的因此安全性就不能得到保證了。