Java類的生命周期淺析


類的生命周期?對象的生命周期?Spring bean 的生命周期?很多同學可能在學習java基礎知識之初,很容易把這幾個搞混。本文先來說說Java類的生命周期

知識前提


在了解類的生命周期之前,有必要先了解一下jvm的內存結構。如下所示:

image

在了解完jvm的內存結構之后,我們就知道了例如堆區,棧區,常量池和方法區等概念。

也了解到了,我們編寫的代碼,是先需要通過編譯的,轉化成.class文件,才能夠被jvm所加載運行的。那簡單來說,java類被jvm進行加載到卸載的過程,就是java類的一生,我們稱之為java類的生命的周期


類的生命周期


一個類完整的生命周期,會經歷五個階段,分別為:加載、連接、初始化、使用、和卸載。其中的連接又分為驗證、准備解析三個步驟。如下圖所示:

也可能會存在加載或連接之后就直接別使用的情況,這里后續討論

image

也可以說:Java類從被加載到虛擬機內存中開始,到卸載出內存為止,它的整個生命周期包括:加載(Loading)、驗證(Verification)、准備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using) 和 卸載(Unloading)七個階段


加載(Loading)

簡單一句話概括,類的加載階段就是:找到需要加載的類並把類的信息加載到jvm的方法區中,然后在堆區中實例化一個java.lang.Class對象,作為方法區中這個類的信息的入口。結合jvm的內存結構會比較好理解。

這里要區別一下接觸到的類加載。類加載其實包括加載、連接、初始化三個階段。類加載強調一個jvm能夠直接使用所需的類,所以類必須完成初始化。

不同的虛擬機對類的加載時機有不同的實現方式,具體要看虛擬機的實現方式。這里不做展開。

類的加載方式比較靈活,總結下來有如下幾種:

  1. 據類的全路徑名找到相應的class文件,然后從class文件中讀取文件內容;(常用)
  2. 從jar文件中讀取。另外,還有下面幾種方式也比較常用:(常用)
  3. 從網絡中獲取:比如10年前十分流行的Applet。
  4. 根據一定的規則實時生成,比如設計模式中的動態代理模式,就是根據相應的類自動生成它的代理類。
  5. 從非class文件中獲取,其實這與直接從class文件中獲取的方式本質


連接(Linking)
  1. 驗證:進行類的合法性校驗。會對比如字節碼格式、變量與方法的合法性、數據類型的有效性、繼承與實現的規范性等等進行檢查,確保別加載的類能夠正常的被jvm所正常運行。

  2. 准備:為類的靜態變量分配內存,並設為jvm默認的初值;對於非靜態的變量,則不會為它們分配內存。簡單說就是分內存、賦初值

    注意:設置初始值為jvm默認初值,而不是程序設定。規則如下

    • 基本類型(int、long、short、char、byte、boolean、float、double)的默認值為0
    • 引用類型的默認值為null
    • 常量的默認值為我們程序中設定的值,比如我們在程序中定義final static int a = 100,則准備階段中a的初值就是100。
  3. 解析:這一階段的任務就是把常量池中的符號引用轉換為直接引用。



初始化(Initialization)

類初始化階段是類加載過程的最后一步。而也是到了該階段,才真正開始執行類中定義的java程序代碼(字節碼),之前的動作都由虛擬機主導。

jvm對類的加載時機沒有明確規范,但對類的初始化時機有:只有當類被直接引用的時候,才會觸發類的初始化。類被直接引用的情況有以下幾種:

  1. 通過以下幾種方式:

    • new關鍵字創建對象

    • 讀取或設置類的靜態變量

    • 調用類的靜態方法

  2. 通過反射方式執行1里面的三種方式;

  3. 初始化子類的時候,會觸發父類的初始化;

  4. 作為程序入口直接運行時(調用main方法);

  5. 接口實現類初始化的時候,會觸發直接或間接實現的所有接口的初始化。

關於類的初始化,記住兩句話

1、類的初始化,會自上而下運行靜態代碼塊或靜態賦值語句,非靜態與非賦值的靜態語句均不執行。

2、如果存在父類,則父類先進行初始化,是一個典型的遞歸模型。

區別於對象的初始化,類的初始化所做的一起都是基於類變量或類語句的,也就是說執行的都是共性的抽象信息。而我們知道,類就是對象實例的抽象。



使用(Using)

類的使用分為直接引用間接引用

直接引用與間接引用等判別條件,是看對該類的引用是否會引起類的初始化

直接引用已經在類的初始化中的有過闡述,不再贅述。而類的間接引用,主要有下面幾種情況:

  1. 當引用了一個類的靜態變量,而該靜態變量繼承自父類的話,不引起初始化
  2. 定義一個類的數組,不會引起該類的初始化;
  3. 當引用一個類的的常量時,不會引起該類的初始化


卸載((Unloading)

當類使用完了之后,類就要進入卸載階段了。那何為衡量類使用完的標准呢?

  1. 該類所有的實例都已經被回收,也就是java堆中不存在該類的任何實例。
  2. 加載該類的ClassLoader已經被回收。
  3. 該類對應的java.lang.Class對象沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法。

如果以上三個條件全部滿足,jvm就會在方法區垃圾回收的時候對類進行卸載,類的卸載過程其實就是在方法區中清空類信息,java類的整個生命周期就結束了。



參考:詳解java類的生命周期


免責聲明!

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



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