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

簡介

有了java class文件以後,爲了讓class文件轉換成爲JVM能夠真正運行的結構,須要經歷加載,連接和初始化的過程。java

這三個過程是怎麼工做的呢?在本文中你將會找到答案。數組

加載

JVM能夠分爲三大部分,五大空間和三大引擎,要講起來也不是特別複雜,先看下面的整體的JVM架構圖。架構

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

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

運行時常量池

咱們知道JVM中有一個方法區的區域,在JDK8中,方法區的實現叫作元空間。這個元空間是存放在本地內存中的。對象

方法區中存放着每一個class對應的運行時常量池。blog

當類或者接口建立的時候,就會經過class文件中定義的常量池來構建運行時常量池。接口

運行時常量池中有兩種類型,分別是symbolic references符號引用和static constants靜態常量。內存

其中靜態常量不須要後續解析,而符號引用須要進一步進行解析處理。ci

靜態常量分爲兩個部分: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這些指令的時候,都會去將符號引用指向運行時常量池,從而須要對符號引用進行解析。

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

初始化

類或者接口的初始化是指執行類或者接口的初始化方法<clinit>。

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

  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的博客

歡迎關注個人公衆號:程序那些事,更多精彩等着您!

相關文章
相關標籤/搜索