1. 程式人生 > >Java類的生命周期

Java類的生命周期

完全 類的加載 ref 修飾 生命 創建對象 除了 區別 類對象

本文轉載自:https://blog.csdn.net/zhengzhb/article/details/7517213

我們在編寫好java文件後會對其進行編譯,稱為class字節碼文件,而Java生命周期指的就是一個class文件從加載到卸載的全過程。一個java類的完整的生命周期會經歷加載、連接、初始化、使用、和卸載五個階段,如下圖所示:
技術分享圖片

下面,就針對幾個階段展開說明。

加載

說到加載,首先在這裏區分一下其與經常提及的類加載的區別:
類加載指的是類的生命周期中加載、連接、初始化三個階段,在加載階段,java虛擬機會找到需要加載的類並把類的信息加載到jvm的方法區中,然後在堆區中實例化一個java.lang.Class對象,作為方法區中這個類的信息的入口。
常見的類的加載方式有以下幾種:本地路徑下編譯生成的.class文件、jar包中的.class文件、遠程網絡獲取(Applet),以及動態代理實時編譯(動態代理模式)。
對於加載的時機,各個虛擬機的做法並不一樣,但是有一個原則,就是當jvm“預期”到一個類將要被使用時,就會在使用它之前對這個類進行加載。比如說,在一段代碼中出現了一個類的名字,jvm在執行這段代碼之前並不能確定這個類是否會被使用到,於是,有些jvm會在執行前就加載這個類,而有些則在真正需要用的時候才會去加載它,這取決於具體的jvm實現。我們常用的hotspot虛擬機是采用的後者,就是說當真正用到一個類的時候才對它進行加載。
加載階段是類的生命周期中的第一個階段,加載階段之後,是連接階段。有一點需要註意,就是有時連接階段並不會等加載階段完全完成之後才開始,而是交叉進行,可能一個類只加載了一部分之後,連接階段就已經開始了。但是這兩個階段總的開始時間和完成時間總是固定的:加載階段總是在連接階段之前開始,連接階段總是在加載階段完成之後完成。

連接

連接階段比較復雜,一般會跟加載階段和初始化階段交叉進行,這個階段的主要任務就是做一些加載後的驗證工作以及一些初始化前的準備工作,可以細分為三個步驟:驗證、準備和解析。

  1. 驗證:當一個類被加載之後,必須要驗證一下這個類是否合法,包括類是不是符合字節碼的格式、變量與方法是不是有重復、數據類型是不是有效、繼承與實現是否合乎標準等。這個階段的目的就是保證加載的類是能夠被jvm所運行。
  2. 準備:準備階段的工作就是為類的靜態變量分配內存並設為jvm默認的初值,對於非靜態的變量,則不會為它們分配內存。需要註意的是,這時候,靜態變量的初值為jvm默認的初值,而不是我們在程序中設定的初值。jvm默認的初值是這樣的:
    • 基本類型(int、long、short、char、byte、boolean、float、double)的默認值為0。
    • 引用類型的默認值為null。
    • 常量的默認值為我們程序中設定的值,比如我們在程序中定義final static int a = 100,則準備階段中a的初值就是100。
  3. 解析:這一階段的任務就是把常量池中的符號引用轉換為直接引用。如在內存中找一個類裏面的一個叫做show的方法,在解析階段,jvm就會把show這個名字轉換為指向方法區的的一塊內存地址,比如c17164,通過c17164就可以找到show這個方法具體分配在內存的哪一個區域了。這裏show就是符號引用,而c17164就是直接引用。在解析階段,jvm會將所有的類或接口名、字段名、方法名轉換為具體的內存地址。

連接階段完成之後會根據使用的情況(直接引用還是被動引用)來選擇是否對類進行初始化。

初始化

如果一個類被直接引用,就會觸發類的初始化。在java中,直接引用的情況有:

  • 通過new關鍵字實例化對象、讀取或設置類的靜態變量、調用類的靜態方法。
  • 通過反射方式執行以上三種行為。
  • 初始化子類的時候,會觸發父類的初始化。
  • 作為程序入口直接運行時(也就是直接調用main方法)。

除了以上四種情況,其他使用類的方式叫做被動引用,而被動引用不會觸發類的初始化。
類的初始化過程是這樣的:按照順序自上而下運行類中的變量賦值語句和靜態語句,如果有父類,則首先按照順序運行父類中的變量賦值語句和靜態語句。
在類的初始化階段,只會初始化與類相關的靜態賦值語句和靜態語句,也就是有static關鍵字修飾的信息,而沒有static修飾的賦值語句和執行語句在實例化對象的時候才會運行。

使用

類的使用包括主動引用和被動引用,主動引用在初始化已經提及,下面我們主要來說一下被動引用:

  • 引用父類的靜態字段,只會引起父類的初始化,而不會引起子類的初始化。
  • 定義類數組,不會引起類的初始化。
  • 引用類的常量,不會引起類的初始化。

使用階段包括主動引用和被動引用,主動飲用會引起類的初始化,而被動引用不會引起類的初始化。當使用階段完成之後,java類就進入了卸載階段。

卸載

在類使用完之後,如果滿足下面的情況,類就會被卸載:

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

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

結語

Java對象基本上都是在jvm的堆區中創建,在創建對象之前,會觸發類加載(加載、連接、初始化),當類初始化完成後,根據類信息在堆區中實例化類對象,初始化非靜態變量、非靜態代碼以及默認構造方法,當對象使用完之後會在合適的時候被jvm垃圾收集器回收。讀完本文後我們知道,對象的生命周期只是類的生命周期中使用階段的主動引用的一種情況(即實例化類對象)。而類的整個生命周期則要比對象的生命周期長的多。

Java類的生命周期