這一節咱們來總結一下JVM類加載機制。具體目錄以下:
java
虛擬機把描述類的數據從Class文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終造成能夠被虛擬機直接使用的Java類型,這就是Java虛擬機的類加載機制。程序員
在Java中,類型的加載、鏈接和初始化過程都在程序運行期間完成的,這種策略雖然會使類加載時增長一些性能開銷,可是提供了高度的靈活性,Java裏天生能夠動態擴展的語言特就是依賴於運行期動態加載和動態鏈接的特色實現的。
Class文件指的是一串二進制的字節流。實際上,每一個Class文件都有可能表明着Java語言中的一個類或者接口。數組
在這七個過程當中,加載、驗證、準備、初始化、卸載這5個階段的順序是必定的,類的加載過程必須按照這種順序循序漸進地開始,而解析過程則不必定:它在某個狀況下能夠在初始化階段以後再開始,這是爲了支持Java語言語言的運行時綁定(也叫動態綁定和晚期綁定)。
這裏強調的是:類加載階段都是互相交叉地混合式進行的,一般是在一個階段執行的過程當中調用、激活另外一階段。安全
對類的初始化操做可分爲主動引用和被動引用
主動引用:在如下5種狀況下會進行類的主動引用的初始化操做:數據結構
值得注意的是:
接口也有本身的初始化過程:編譯器會爲接口生成「()」類構造器,用於初始化接口中所定義的成員變量。
接口和類初始化的區別:當一個類在初始化時,其父類都基本上初始化過了,然而接口在初始化的時候,只有真正用到父接口的時候(如引用接口中定義的常量)纔會進行初始化。性能
在加載階段,虛擬機須要完成如下3件事情:spa
非數組類的加載是可控性最強的。用戶除了使用系統提供的引導類加載類來完成,也能夠由用戶自定義的類加載器去加載(重寫一個類加載器的loadClass())。
注意:數組類自己不經過類加載器建立,它是由JVM直接建立的。但數組類和類加載器仍有很緊密的關係,由於數組類的元素類型最終是靠類加載器去建立。
加載完成後,虛擬機外部的二進制字節流就按照虛擬機所需格式存儲在方法區之中,方法區中的數據存儲格式由虛擬機實現自行定義。而後在內存中實例化一個java.lang.Class類的對象(能夠在Java堆中,也能夠在方法區中),該對象將做爲程序去訪問方法區中的這些類型數據的外部接口。
加載階段與鏈接階段的部份內容(如一部分字節碼文件格式驗證動做)是交叉進行的,加載階段還沒有結束,鏈接階段就可能開始了。可是夾在加載階段進行的動做,仍然屬於鏈接階段的內容。指針
驗證是鏈接的第一步,目的是爲了確保Class文件的字節流中包含的信息符合當前虛擬機的要求,而且不會危及虛擬機自己的安全。
驗證階段的四個步驟:文件格式檢驗、元數據檢驗、字節碼檢驗、符號引用檢驗。對象
準備階段是正式爲類變量分配內存並設置類變量初始值的階段。這些變量所使用的內存將在方法區中進行分配。此時進行內存分配的僅包括類變量(被static修飾的變量),而不包括實例變量,實例變量將會在對象實例化時隨着對象一塊兒分配在Java堆中。另外,在這裏分配的靜態類變量是將其值定義爲0等默認值,而不是咱們定義的。由於這時還沒有執行任何Java方法,咱們定義的賦值的putStatic指令是程序被編譯後,存放在類構造器()方法中,因此正確的賦值將在初始化階段執行。
若是類變量被final修飾,那麼在這種狀況下,在編譯時Javac將會爲該變量生成ConstantValue屬性,在準備階段虛擬機會根據該屬性設置類變量的正確值。繼承
解析階段是虛擬機將常量池內的符號引用替換爲直接引用的過程。
a、符號引用:以一組符號來描述所引用的目標,符號能夠是任何形式字面量,只要使用時無歧義地定位到目標就行。
b、 直接引用:直接引用是直接指向目標的指針、相對偏移量或是一個能間接定位到目標的句柄。引用的目標已經在內存中存在。
虛擬機實現能夠根據須要來判斷到底在類被加載器加載時就對常量池中的符號引用進行解析,仍是等到一個符號引用將要被使用時纔去解析它。解析動做主要針對類或接口、字段、類方法、接口方法、方法類型、方法句柄和調用點限定符7類符號引用進行。
類加載的最後一步,真正執行類中定義的Java程序代碼(字節碼)。 初始化階段是執行類構造器()方法的過程,根據程序員經過程序制定的主觀的計劃去初始化類變量和其餘資源。