JVM詳解之:類的加載鏈接和初始化


簡介

有了java class文件之后,為了讓class文件轉換成為JVM可以真正運行的結構,需要經歷加載,鏈接和初始化的過程。

這三個過程是怎么工作的呢?在本文中你將會找到答案。

加載

JVM可以分為三大部分,五大空間和三大引擎,要講起來也不是特別復雜,先看下面的總體的JVM架構圖。

從上面的圖中,我們可以看到JVM中有三大部分,分別是類加載系統,運行時數據區域和Execution Engine。

加載就是根據特定名稱查找類或者接口的二進制表示,並根據此二進制表示來創建類和接口的過程。

運行時常量池

我們知道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。

類加載器

類是怎么創建的呢?類的創建可以是由其他類調用該類的初始化方法來創建,也可以通過反射來創建。

類其實又可以分為兩種,一種是數組類,一種是非數組類。

對於非數組類,因為他們有相應的二進制表示,所以是通過類加載器加載二進制表示來創建的。

而對於數組類,因為他們沒有外部的二進制表示,所以數組類是由java虛擬機創建的。

java虛擬機中的類加載器又有兩種,一種是虛擬機提供的引導類加載器,一種是用戶自定義的類加載器。

如果是用戶自定的類加載器,那么應該是ClassLoader的一個實現。用戶自定義類加載器主要是為了擴展java虛擬機的功能,以支持動態加載並創建類。

鏈接

鏈接是為了讓類或者接口可以被java虛擬機執行,而將類或者接口並入虛擬機運行時狀態的過程。

鏈接具體的工作包括驗證和准備類或者接口。而解析這個類或者接口中的符號引用是鏈接過程中的可選部分。

如果java虛擬機選擇在用到類或者接口中的符號引用時才去解析他們,這叫做延遲解析。

如果java虛擬機在驗證類的時候就解析符號引用,這就叫做預先解析。

驗證

驗證主要是為了保證類和接口的二進制表示的結構正確性。

如果類或者接口的二進制表示不滿足相應的約束,則會拋出VerifyError異常。

准備

准備主要是創建類或者接口的靜態字段,並使用默認值來初始化這些字段。

解析

解析是指根據運行時常量池中的符號引用來動態決定其具體值的過程。

在執行java虛擬機指令:

anewarray,checkcat, getfield, getstatic, instanceof, invokedynamic, invokeinterface, invokespecial, invokestatic, invokevirtual, ldc, ldc_w, multianewarray, new , putfield和putstatic這些指令的時候,都會去將符號引用指向運行時常量池,從而需要對符號引用進行解析。

解析可以分為類和接口的解析,字段解析,普通方法的解析,接口方法解析,方法類型和方法句柄解析,調用點限定符解析這幾種。

初始化

類或者接口的初始化是指執行類或者接口的初始化方法

只有下面的幾種情況,類或者接口才會被初始化:

  1. 執行需要引用類或者接口的java虛擬機指令(new,getstatic, putstatic, invokestatic)的時候。
  2. 初次調用java.lang.invoke.Methodhandle實例的時候。
  3. 調用類庫中的某些反射方法的時候。
  4. 對類的某個子類進行初始化的時候。
  5. 被選定為java虛擬機啟動時候的初始類的時候。

總結

class文件經過加載,鏈接和初始化之后,就可以提供給JVM在運行時使用了。

本文作者:flydean程序那些事

本文鏈接:http://www.flydean.com/jvm-class-load-link-ini/

本文來源:flydean的博客

歡迎關注我的公眾號:程序那些事,更多精彩等着您!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM