1. 程式人生 > >Java的類載入過程

Java的類載入過程

一個Java檔案從編碼完成到最終執行,一般主要包括兩個過程:編譯與執行。編譯即將Java檔案通過Javac命令生成.class檔案的過程,執行就是將.class檔案交給JVM進行執行。
類載入過程即是指JVM虛擬機器把.class檔案中類資訊載入進記憶體,並進行解析生成對應的class物件的過程。JVM在執行某段程式碼時,遇到了class A,然而此時記憶體中並沒有class A的相關資訊,於是JVM就會到相應的class檔案中去尋找class A的類資訊,並載入進記憶體中,這就是類載入過程。
因此,JVM不是一開始就把所有的類都載入進記憶體中,而是隻有第一次遇到某個需要執行的類時才會載入,且只加載一次。
類載入的過程分為三個過程:載入、連線、初始化

載入

載入:載入指的是把class位元組碼檔案從各個來源通過類載入器裝載入記憶體中。
位元組碼的來源:本地路徑下編譯生成的.class檔案,jar包中的.class檔案,遠端網路獲取,以及動態代理實時編譯
類載入器:一般包括啟動類載入器,擴充套件類載入器,應用類載入器,以及使用者的自定義類載入器。
允許自定義類載入器的原因?
一方面是由於java程式碼很容易被反編譯,如果需要對自己的程式碼加密的話,可以對編譯後的程式碼進行加密,然後再通過實現自己的自定義類載入器進行解密,最後再載入。另一方面也有可能從非標準的來源載入程式碼,比如從網路來源,那就需要自己實現一個類載入器,從指定源進行載入。

連線

連線又可以細分為三個小部分:驗證、準備、解析

驗證:保證載入進來的位元組流符合虛擬機器規範,不會造成安全錯誤
包括對於檔案格式的驗證,比如常量中是否有不被支援的常量?檔案中是否有不規範的或者附加的其他資訊?
對於元資料的驗證,比如該類是否繼承了被final修飾的類?類中的欄位,方法是否與父類衝突?是否出現了不合理的過載?
對於位元組碼的驗證,保證程式語義的合理性,比如要保證型別轉換的合理性。
對於符號引用的驗證,比如校驗符號引用中通過全限定名是否能夠找到對應的類?校驗符號引用中的訪問性(private,public等)是否可被當前類訪問?

準備:為類變數(注意,不是例項變數)分配記憶體,並且賦予初值
特別需要注意,初值,不是程式碼中具體寫的初始化的值,而是Java虛擬機器根據不同變數型別的預設初始值。比如8種基本型別的初值,預設為0;引用型別的初值則為null;常量的初值即為程式碼中設定的值,final static tmp = 456, 那麼該階段tmp的初值就是456

解析:將常量池內的符號引用替換為直接引用的過程。
符號引用。即一個字串,但是這個字串給出了一些能夠唯一性識別一個方法,一個變數,一個類的相關資訊。
直接引用。可以理解為一個記憶體地址,或者一個偏移量。比如類方法,類變數的直接引用是指向方法區的指標;而例項方法,例項變數的直接引用則是從例項的頭指標開始算起到這個例項變數位置的偏移量
舉例來說,現在呼叫方法hello(),這個方法的地址是1234567,那麼hello就是符號引用,1234567就是直接引用。在解析階段,虛擬機器會把所有的類名,方法名,欄位名這些符號引用替換為具體的記憶體地址或偏移量,也就是直接引用。

初始化

初始化:這個階段主要是對類變數初始化,是執行類構造器的過程。
換句話說,只對static修飾的變數或語句進行初始化。如果初始化一個類的時候,其父類尚未初始化,則優先初始化其父類。如果同時包含多個靜態變數和靜態程式碼塊,則按照自上而下的順序依次執行。

類載入過程只是一個類生命週期的一部分,在其前,有編譯的過程,只有對原始碼編譯之後,才能獲得能夠被虛擬機器載入的位元組碼檔案;在其後還有具體的類使用過程,當使用完成之後,還會在方法區垃圾回收的過程中進行解除安裝。

本文參考自:
https://blog.csdn.net/ln152315/article/details/79223441