從源碼理解類載入
我們都知道,我們得java程式得運行,實際是根據面向對象編程得原理,為一個個類創建對象,對象們協同工作,完成了程式得運行!
但是這些類,說到底是一個個得文件,二進位的class,如何變成為jvm所用的對象呢?,我們稱之為類載入!看classloader偶有心得,隨筆以記!
1 類載入原理(雙親委派機制)
說到雙親委派機制,那就祭出一張十分眼熟得圖!
這是一張很簡單易懂得圖!就是類載入是有父子關係得,當接受到類載入請求時,都是先去找自己得「爸爸」類載入去完成載入,找到則返回,找不到則跑錯!可參考下圖源碼理解:
上圖得這段程式碼就是classload種得loadClass方法實現! 上圖可見,根據一個name載入類時,第一步會根據name得到一個鎖,保證類載入得並發安全!第二步就是判斷父類載入器是否為空,如果不為空,這調用父類載入得loadClass方法載入,如果為空,則第三步使用根載入器BootStrap根據名稱載入,如果以上步驟完成後,依然沒載入到類,在第四步調用findClass(name),實際是拋出一個 ClassNotFoundException(name)的異常!
類載入器主要有四種,根載入,擴展載入,應用載入,自定義載入,其父子順序主要可見 sun.misc.Lancher中程式碼:
在Lancher的構造函數中,首先創建了ExtClassLoader 擴展載入,沒有傳遞parent載入器,則為根載入器BootStrap載入器為parent,而後又將ExtClassLoader 做為parent屬性,用以創建了AppClassLoader應用載入!
在Lancher中有一個根目錄屬性 bootClassPath,通過 System.getProperty(“sun.boot.class.path”); 如果將其列印,可以發現是jdk下的lib下的一些jar包和classee目錄!在Lancher中的靜態內部類ExtClassLoader 實現了擴展載入,在調用getExtClassLoader時會調用createExtClassLoader方法給單例模式的ExtClassLoader instrance創建,在createExtClassLoader方法中可見調用了 getExtDirs()方法,實際是通過System.getProperty(“java.ext.dirs”)獲取擴展載入的類,列印可見該目錄是jre\lib\ext目錄。
在Lancher的靜態內部類 AppClassLoader中實現了應用載入,在調用 getAppClassLoader方法獲取應用載入器的程式碼中可見調用了System.getProperty(“java.class.path”) 獲取了開發人員指定的resource路徑或默認路徑。
在這個完整的模式下,載入的類可以被它的類載入器快取,沒必要重複載入,同時,如果定義了同一個名稱的兩個類,也只會載入一個,保證了安全。
通過閱讀ClassLoader的程式碼可見,自定義類載入,實現ClassLoader即可,通過調用loadclass實現類載入! 如果想打破雙親委派載入模型,在自己重寫的loadClass方法中實現即可。
那麼把一個class文件從jar包中讀取生成class對象,又可見以下圖:
載入: 通過類的全限定名獲取二進位位元組流,把這個位元組流代表的靜態存儲結構轉化為方法區的運行時數據介面,在記憶體中創建一個Class對象作為方法區內這個類的訪問入口。
驗證:1 文件格式驗證:
a class文件以0xCAFEBABE(咖啡baby)開頭
b 版本號是否在當前虛擬機可接受範圍
c 常量池中常量是否有不支援的類型
……..
2 元數據驗證 : 主要是驗證語義,語法是否複合java語言規範,比如 是否有父類,父類是否是被final修飾過,如果不是抽象類,是否已完全實現抽象方法等
3 位元組碼驗證 : 主要是驗證語義預發是否合乎邏輯,注意,是邏輯,不同2中的規範。比如long類型數據被以int載入到本地
4 符合引用驗證,驗證類中用到的引用是否自己有通過全限定名訪問的許可權
準備:為類中定義的靜態變數分配記憶體
解析:解析內部的欄位和方法,把符號引用替換為直接引用
初始化 :靜態變數獲取到了指定句柄,既指定值,執行靜態程式碼。
以上為類載入的過程,那合適會出發類載入呢?有以下條件觸發:
1 遇到new getstatic putstatic invokestatic四個指令時,如果沒有初始化,則需要觸發初始化。這4個指令通常是new 實例化對象,讀取或設置static屬性,調用靜態方法是。
2 對一個類使用反射包下方法調用時
3 初始化一個類,但是發現起父類沒有初始化時
4 虛擬機啟動時,執行一個主類