您好,登錄后才能下訂單哦!
這篇文章給大家介紹JVM中類的加載鏈接和初始化是怎么樣的,內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
簡介
有了java class文件之后,為了讓class文件轉換成為JVM可以真正運行的結構,需要經歷加載,鏈接和初始化的過程。
這三個過程是怎么工作的呢?在本文中你將會找到答案。
加載
JVM可以分為三大部分,五大空間和三大引擎,要講起來也不是特別復雜,先看下面的總體的JVM架構圖。
從上面的圖中,我們可以看到JVM中有三大部分,分別是類加載系統,運行時數據區域和Execution Engine。
加載就是根據特定名稱查找類或者接口的二進制表示,并根據此二進制表示來創建類和接口的過程。
01
運行時常量池
我們知道JVM中有一個方法區的區域,在JDK8中,方法區的實現叫做元空間。這個元空間是存放在本地內存中的。
方法區中存放著每個class對應的運行時常量池。
當類或者接口創建的時候,就會通過class文件中定義的常量池來構建運行時常量池。
運行時常量池中有兩種類型,分別是symbolic references符號引用和static constants靜態常量。
其中靜態常量不需要后續解析,而符號引用需要進一步進行解析處理。
靜態常量分為兩個部分:String常量和數字常量。
String常量是對String對象的引用,是從class中的CONSTANT_String_info結構體構建的。
數字常量是從class文件中的CONSTANT_Integer_info, CONSTANT_Float_info, CONSTANT_Long_info和 CONSTANT_Double_info 構建的。
符號引用也是從class中的constant_pool中構建的。
對class和interface的符號引用來自于CONSTANT_Class_info。
對class和interface中字段的引用來自于CONSTANT_Fieldref_info。
class中方法的引用來自于CONSTANT_Methodref_info。
interface中方法的引用來自于CONSTANT_InterfaceMethodref_info。
對方法句柄的引用來自于CONSTANT_MethodHandle_info。
對方法類型的引用來自于CONSTANT_MethodType_info。
對動態計算常量的符號引用來自于CONSTANT_MethodType_info。
對動態計算的call site的引用來自于CONSTANT_InvokeDynamic_info。
02
類加載器
類是怎么創建的呢?類的創建可以是由其他類調用該類的初始化方法來創建,也可以通過反射來創建。
類其實又可以分為兩種,一種是數組類,一種是非數組類。
對于非數組類,因為他們有相應的二進制表示,所以是通過類加載器加載二進制表示來創建的。
而對于數組類,因為他們沒有外部的二進制表示,所以數組類是由java虛擬機創建的。
java虛擬機中的類加載器又有兩種,一種是虛擬機提供的引導類加載器,一種是用戶自定義的類加載器。
如果是用戶自定的類加載器,那么應該是ClassLoader的一個實現。用戶自定義類加載器主要是為了擴展java虛擬機的功能,以支持動態加載并創建類。
鏈接
鏈接是為了讓類或者接口可以被java虛擬機執行,而將類或者接口并入虛擬機運行時狀態的過程。
鏈接具體的工作包括驗證和準備類或者接口。而解析這個類或者接口中的符號引用是鏈接過程中的可選部分。
如果java虛擬機選擇在用到類或者接口中的符號引用時才去解析他們,這叫做延遲解析。
如果java虛擬機在驗證類的時候就解析符號引用,這就叫做預先解析。
01
驗證
驗證主要是為了保證類和接口的二進制表示的結構正確性。
如果類或者接口的二進制表示不滿足相應的約束,則會拋出VerifyError異常。
02
準備
準備主要是創建類或者接口的靜態字段,并使用默認值來初始化這些字段。
03
解析
解析是指根據運行時常量池中的符號引用來動態決定其具體值的過程。
在執行java虛擬機指令:
anewarray,checkcat, getfield, getstatic, instanceof, invokedynamic, invokeinterface, invokespecial, invokestatic, invokevirtual, ldc, ldc_w, multianewarray, new , putfield和putstatic這些指令的時候,都會去將符號引用指向運行時常量池,從而需要對符號引用進行解析。
解析可以分為類和接口的解析,字段解析,普通方法的解析,接口方法解析,方法類型和方法句柄解析,調用點限定符解析這幾種。
初始化
類或者接口的初始化是指執行類或者接口的初始化方法<clinit>。
只有下面的幾種情況,類或者接口才會被初始化:
執行需要引用類或者接口的java虛擬機指令(new,getstatic, putstatic, invokestatic)的時候。
初次調用java.lang.invoke.Methodhandle實例的時候。
調用類庫中的某些反射方法的時候。
對類的某個子類進行初始化的時候。
被選定為java虛擬機啟動時候的初始類的時候。
class文件經過加載,鏈接和初始化之后,就可以提供給JVM在運行時使用了。
關于JVM中類的加載鏈接和初始化是怎么樣的就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。