類的加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放在運行時數據區的方法區內,而後在堆區建立一個java.lang.Class對象,用來封裝類在方法區內的數據結構。 java
類的加載並非首次主動使用時加載,而是JVM在「預料」到某個類將要使用時預先加載。若是在預先加載的過程當中遇到了.class文件缺失或存在錯誤,類加載器在程序首次主動使用該類時才報錯(LinkageError錯誤),若是這個類一直沒有被主動使用,那麼類加載器就不會報錯。數組
加載.class文件時,能夠從本地直接加在;能夠從zip包中讀取;能夠運行時動態代理生成;也能夠從網絡加載;及其餘文件轉換(如jsp)。安全
JVM類加載分五個階段:加載,驗證,準備,解析,初始化。網絡
加載、驗證、準備和初始化這四個階段發生的順序是固定的,而爲了支持動態綁定,解析階段在某些狀況下能夠在初始化階段以後開始。同時這幾個階段是按順序開始,而不是按順序進行或完成,這些階段一般都是互相交叉地混合進行的,在一個階段執行的過程當中調用或激活另外一個階段。數據結構
加載:這個階段會在內存中生成一個表明這個類的java.lang.Class對象,做爲方法區這個類的各類數據的入口。jvm
驗證:驗證Class文件的字節流中包含的信息是否符合當前虛擬機的要求,而且不會危害虛擬機自身的安全。驗證階段不是必須的,它對程序運行期沒有影響,若是所引用的類通過反覆驗證,那麼能夠考慮採用-Xverify:none參數來關閉大部分的類驗證措施,以縮短虛擬機類加載的時間。jsp
準備:正式爲類變量分配內存並設置類變量的初始值階段,即在方法區中分配這些變量所使用的內存空間。佈局
此時進行內存分配的僅包括類變量(static),而不包括實例變量,實例變量會在對象實例化時隨着對象一塊分配在Java堆中spa
這裏的初始值是數據類型默認的零值(如0、null、false等)而不是代碼裏面的初始化參數時所賦的值。代理
解析:解析階段主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用點限定符7類符號引用,虛擬機將常量池中的符號引用替換爲直接引用。
符號引用以一組符號來描述所引用的目標,這些信息必須足以惟一的識別一個類、字段、方法;直接引用能夠是指向目標的指針,相對偏移量或是一個能間接定位到目標的句柄。
不一樣jvm的內存佈局可能不一樣,直接引用與jvm的內存佈局相關,而符號引用與內存佈局不相關。
初始化:初始化階段是執行類構造器<clint>
方法的過程。<clint>
方法是由編譯器自動收集類中的類變量的賦值操做和靜態語句塊中的語句合併而成的。虛擬機會保證<clint>
方法執行以前,父類的<clint>
方法已經執行完畢。
類加載器
JVM提供了三種類加載器:
Bootstrap ClassLoader
):負責加載 JAVA_HOME\lib 目錄中的,或經過-Xbootclasspath
參數指定路徑中的,且被虛擬機承認(按文件名識別,如rt.jar)的類,由C++實現,不是ClassLoader的子類。Extension ClassLoader
):負責加載 JAVA_HOME\lib\ext 目錄中的,或經過java.ext.dirs系統變量指定路徑中的類庫,java實現,ClassLoader的子類。Application ClassLoader
):負責加載用戶路徑(classpath)上的類庫,java實現,ClassLoader的子類。以及自定義類加載器,應用程序根據自身須要自定義的類加載器。
JVM經過雙親委派模型進行類的加載:若是一個類加載器收到了類加載的請求,它首先不會本身去嘗試加載這個類,而是把請求委託給父加載器去完成,依次向上,所以,全部的類加載請求最終都應該被傳遞到頂層的啓動類加載器中,只有當父加載器在它的搜索範圍中沒有找到所需的類時,即沒法完成該加載,子加載器纔會嘗試本身去加載該類。
採用雙親委派模型,無論是哪一個加載器加載這個類,最終都是委託給頂層類加載器進行加載,防止內存中出現多份一樣的字節碼。
雙親委派機制僅僅是java 規範所推薦的一種實現方式,並非強制性的要求,如OSGI就採用網絡圖莊的類加載模型,而非雙親委派機制。相比而言網狀的類加載模型比雙親委派的層次化模型在加載類時效率更高,無需在classpath一長串列表中進行查找;同時錯誤提示更加友好。