java類生命週期,類的“加載,連接,初始化,使用,卸載過程”詳解

 如果說核心類庫的 API 比做數學公式的話,那麼 Java 虛擬機的知識就好比公式的推導過程

每本Java入門書籍在介紹Java這門語言的時候都會提到Java跨平臺,“一次解釋,到處運行的特點“,功臣就是jvm(Java Virtual Machine,Java虛擬機)。

但是,如果將jvm只與Java語言綁定在一起,那麼理解就過於狹隘了,Java虛擬機發展到現在已經脫離了Java語言,形成了一套相對獨立,高性能的執行方案。

 

除了以上提到的幾種語言之外,scala,熱門的kotlin都可以運行在jvm上面。

 

01 類生命週期

 

類從被加載到虛擬內存中開始,到卸載內存爲止,它的整個生命週期包括:

小提示:

1.加載階段和連接階段有時候是交叉進行的,不需要等到完全加載結束。

2.  解析階段有時候可以再初始化之後再做。Jvm僅僅規定了:如果某些字節碼使用了符號引用,那麼在執行這些字節碼之前,需要完成對這些符號引用的解析。
3.  但是這些過程總的開始時間和完成時間都是上圖固定順序。
4.  這裏的“加載階段”和我們常說的“類加載”是兩回事,“類加載”指的是虛線框中三部分加起來。

 

02 連接(Linking)

驗證:

當一個類被加載之後,必須要驗證一下這個類是否合法,比如這個類是不是符合字節碼的格式、變量與方法是不是有重複、數據類型是不是有效、繼承與實現是否合乎標準等等。

我們平常寫代碼很多時候第一步都是寫校驗,jvm也是這個思路,Java 編譯器生成的類文件必然滿足 Java 虛擬機的約束條件,但是爲了防止“解字節碼注入”。

準備:

  1. 就是爲類的靜態變量分配內存並設爲 jvm默認 的初值,而不是我們設置的,我們設置的會在後面一個階段“初始化”期間來做,對於非靜態的變量,則不會爲它們分配內存。

    jvm默認的初值是這樣的:

    基本類型(int、long、short、char、byte、boolean、float、double)的默認值爲0。其中boolean只有true,false兩種類型,對應到jvm值分別是數據1,0。

    引用類型(對象,數組)的默認值爲null。

  2. 構造其他跟類層次相關的數據結構,比如說用來實現虛方法的動態綁定的方法表。

    在 class 文件被加載至 Java虛擬機之前,這個類無法知道其他類及其方法、字段所對應的具體地址,甚至不知道自己方法、字段的地址。因此,每當需要引用這些成員時,Java 編譯器會生成一個符號引用。在運行階段,這個符號引用一般都能夠無歧義地定位到具體目標上。

解析:

上面說到的“在運行階段,這個符號引用一般都能夠無歧義地定位到具體目標上”,就是在解析階段進行的符號解析。

這個階段目的正是將常量池中的符號引用轉換解析成爲實際引用。在解析階段,jvm會將所有的類或接口名、字段名、方法名轉換爲具體的內存地址,從而讓用到了別的類或者接口的類能找到和加載其他的類/接口。

如果符號引用指向一個未被加載的類,或者未被加載類的字段或方法,那麼解析將觸發這個類的加載(但未必觸發這個類的鏈接以及初始化。)

 

 

03 初始化

在 Java 代碼中,如果要初始化一個靜態字段,我們可以在聲明時直接賦值,也可以在靜態代碼塊中對其賦值。除了final static修飾的常量,直接賦值操作以及所有靜態代碼塊中的代碼,則會被 Java 編譯器置於同一方法中,並把它命名爲 < clinit >。

類加載的最後一步是初始化,目的便是爲標記爲常量值的字段賦值,以及執行 <clinit > 方法的過程。Java 虛擬機會通過加鎖來確保類的 < clinit > 方法僅被執行一次。

類初始化的觸發情況:

  1. 當虛擬機啓動時,初始化用戶指定的主類(main函數);

  2. 當遇到用以新建目標類實例的 new 指令時,初始化 new 指令的目標類;

  3. 當遇到調用靜態方法的指令時,初始化該靜態方法所在的類;

  4. 當遇到訪問靜態字段的指令時,初始化該靜態字段所在的類;

  5. 子類的初始化會觸發父類的初始化;

  6. 如果一個接口定義了 default 方法,那麼直接實現或者間接實現該接口的類的初始化,會觸發該接口的初始化;

  7. 使用反射 API 對某個類進行反射調用時,初始化這個類;

  8. 當初次調用 MethodHandle 實例時,初始化該 MethodHandle 指向的方法所在的類。

設計模式中單例延遲加載,便是充分利用了這個特點。

 

04 卸載

那麼多的類,什麼時候卸載誰呢?關於卸載誰,滿足如下條件:

·      該類所有的實例都已經被回收,也就是java堆中不存在該類的任何實例;

·      加載該類的ClassLoader已經被回收;

·      該類對應的java.lang.Class對象沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法。

 

關於什麼時候卸載,當以上條件都滿足了,垃圾回收時候回在方法區清空類信息進行卸載,英雄遲暮,這個類的一生也就走到了盡頭了。

 

有錯歡迎私我指正

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章