這篇文章我們關注一個問題:Java程式是怎麼進入JVM並執行的?經常寫Java程式的小夥伴應該都聽說過類載入機制,在《深入理解Java虛擬機器》裡周老師已經講的很清楚了,這篇隨筆把之前的筆記以及一些總結重新梳理一下。前面我們已經知道 .java檔案經過編譯後變成Class檔案,JVM載入的是位元組碼檔案。這其中的細節不知道小夥伴們有麼有了解過?
- 通過類的全限定名獲取類的二進位制位元組流
- 將類的靜態儲存結構轉化為方法區的執行時資料結構
- 檔案格式驗證:驗證位元組流是否符合Class檔案格式的規範,並且能被當前版本的JVM處理。只有驗證通過了,位元組流才會進入方法區儲存。
- 後設資料驗證:比如類是否有父類,類是否繼承了不能被繼承的類等,保證不存在不符合Java語言規範的後設資料資訊。
- 位元組碼驗證:對類的方法體進行校驗分析
private static long a = 1;
- 符號引用: 用一組符號描述所引用的目標,引用的目標不一定已經載入到記憶體中。
JVM載入位元組碼檔案靠的是類載入器,這個操作是在JVM外部實現的。這樣應用程式就可以自己決定如何獲取所需的類。如果兩個類來自同一個Class檔案,但是由不同的類載入器載入,那麼者兩個類一定是不相等。從JVM角度講,只有兩種載入器。一種是啟動類載入器,是虛擬機器自身的一部分,由C++語言實現;還有就是其他類載入器,由Java語言實現,全都繼承自抽象類java.lang.ClassLoader 獨立於虛擬機器外部。
從開發角度看,主要分為這三種:
- 啟動類載入器(Bootstrap ClassLoader):載入<JAVA_HOME>/jre/lib目錄中,或者被 -Xbootclasspath引數所指定的路徑中。
- 擴充套件類載入器(Extension ClassLoader):主要載入<JAVA_HOME>/jre/lib目錄中的
- 應用程式類載入器(Application ClassLoader):載入使用者類路徑(ClassPath)上所指定的類庫。如果我們沒有自定義過自己的類載入器,那麼這就是程式預設的類載入器。
這些類載入器之間的關係為:
在使用類載入器載入類的過程種,最好遵循雙親委派模型。雙親委派的原理是:類載入器收到載入類的請求時,先把這個請求委派給父類載入去完成,每一層次的載入器按這個這個邏輯執行。那麼所有的載入請求最終都應該傳送到頂層的啟動類載入中。父載入器無法載入,子載入器才會自己載入。這樣做的好處是可以避免類的重複載入,保證程式執行的穩定性。
我們可以自定義類載入器,總結起來就是:(1)類繼承ClassLoader (2) 重寫findClass() 方法 (3) 呼叫defineClass()方法。在loadClass()裡如果父類載入失敗,呼叫findClass()方法載入,這樣還是符合雙親委派機制的。破壞會雙親委派需要重寫loadClass()方法。
參考資料:《深入理解Java虛擬機器》第二版 周志明
《深入拆解Java虛擬機器》鄭雨迪