類的生命週期?物件的生命週期?Spring bean 的生命週期?很多同學可能在學習java基礎知識之初,很容易把這幾個搞混。本文先來說說Java類的生命週期。
知識前提
在瞭解類的生命週期之前,有必要先了解一下jvm的記憶體結構。如下所示:
在瞭解完jvm的記憶體結構之後,我們就知道了例如堆區,棧區,常量池和方法區等概念。
也瞭解到了,我們編寫的程式碼,是先需要通過編譯的,轉化成.class檔案,才能夠被jvm所載入執行的。那簡單來說,java類被jvm進行載入到解除安裝的過程,就是java類的一生,我們稱之為java類的生命的週期。
類的生命週期
一個類完整的生命週期,會經歷五個階段,分別為:載入、連線、初始化、使用、和解除安裝。其中的連線又分為驗證、準備和解析三個步驟。如下圖所示:
也可能會存在載入或連線之後就直接別使用的情況,這裡後續討論
也可以說:Java類從被載入到虛擬機器記憶體中開始,到解除安裝出記憶體為止,它的整個生命週期包括:載入(Loading)、驗證(Verification)、準備(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using) 和 解除安裝(Unloading)七個階段
載入(Loading)
簡單一句話概括,類的載入階段就是:找到需要載入的類並把類的資訊載入到jvm的方法區中,然後在堆區中例項化一個java.lang.Class物件,作為方法區中這個類的資訊的入口。結合jvm的記憶體結構會比較好理解。
這裡要區別一下接觸到的
類載入
。類載入其實包括載入、連線、初始化三個階段。類載入強調一個jvm能夠直接使用所需的類,所以類必須完成初始化。
不同的虛擬機器對類的載入時機有不同的實現方式,具體要看虛擬機器的實現方式。這裡不做展開。
類的載入方式比較靈活,總結下來有如下幾種:
- 據類的全路徑名找到相應的class檔案,然後從class檔案中讀取檔案內容;(常用)
- 從jar檔案中讀取。另外,還有下面幾種方式也比較常用:(常用)
- 從網路中獲取:比如10年前十分流行的Applet。
- 根據一定的規則實時生成,比如設計模式中的動態代理模式,就是根據相應的類自動生成它的代理類。
- 從非class檔案中獲取,其實這與直接從class檔案中獲取的方式本質
連線(Linking)
-
驗證:進行類的合法性校驗。會對比如位元組碼格式、變數與方法的合法性、資料型別的有效性、繼承與實現的規範性等等進行檢查,確保別載入的類能夠正常的被jvm所正常執行。
-
準備:為類的靜態變數分配記憶體,並設為jvm預設的初值;對於非靜態的變數,則不會為它們分配記憶體。簡單說就是分記憶體、賦初值。
注意:設定初始值為jvm預設初值,而不是程式設定。規則如下
- 基本型別(int、long、short、char、byte、boolean、float、double)的預設值為0
- 引用型別的預設值為null
- 常量的預設值為我們程式中設定的值,比如我們在程式中定義final static int a = 100,則準備階段中a的初值就是100。
-
解析:這一階段的任務就是把常量池中的符號引用轉換為直接引用。
初始化(Initialization)
類初始化階段是類載入過程的最後一步。而也是到了該階段,才真正開始執行類中定義的java程式程式碼(位元組碼),之前的動作都由虛擬機器主導。
jvm對類的載入時機沒有明確規範,但對類的初始化時機有:只有當類被直接引用的時候,才會觸發類的初始化。類被直接引用
的情況有以下幾種:
通過以下幾種方式:
new關鍵字建立物件
讀取或設定類的靜態變數
呼叫類的靜態方法
通過反射方式執行1裡面的三種方式;
初始化子類的時候,會觸發父類的初始化;
作為程式入口直接執行時(呼叫main方法);
介面實現類初始化的時候,會觸發直接或間接實現的所有介面的初始化。
關於類的初始化,記住兩句話
1、類的初始化,會自上而下執行靜態程式碼塊或靜態賦值語句,非靜態與非賦值的靜態語句均不執行。
2、如果存在父類,則父類先進行初始化,是一個典型的遞迴模型。
區別於物件的初始化,類的初始化所做的一起都是基於類變數或類語句的,也就是說執行的都是共性的抽象資訊。而我們知道,類就是物件例項的抽象。
使用(Using)
類的使用分為直接引用和間接引用。
直接引用與間接引用等判別條件,是看對該類的引用是否會引起類的初始化
直接引用已經在類的初始化中的有過闡述,不再贅述。而類的間接引用,主要有下面幾種情況:
- 當引用了一個類的靜態變數,而該靜態變數繼承自父類的話,不引起初始化
- 定義一個類的陣列,不會引起該類的初始化;
- 當引用一個類的的常量時,不會引起該類的初始化
解除安裝((Unloading)
當類使用完了之後,類就要進入解除安裝階段了。那何為衡量類使用完的標準呢?
- 該類所有的例項都已經被回收,也就是java堆中不存在該類的任何例項。
- 載入該類的ClassLoader已經被回收。
- 該類對應的java.lang.Class物件沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法。
如果以上三個條件全部滿足,jvm就會在方法區垃圾回收的時候對類進行解除安裝,類的解除安裝過程其實就是在方法區中清空類資訊,java類的整個生命週期就結束了。
參考:詳解java類的生命週期