Java虛擬機之對象的生命週期

       要在JVM的堆中創建某類型的實例化對象,必須先完成該類型的加載、連接和初始化。因此,對象的生命週期只是類生命週期中的使用階段,而類的生命週期要比對象的生命週期要長。對象的生命週期開始於類的實例化,中間經歷使用階段,結束於對象的垃圾收集。下面主要介紹對象生命週期中的這三個階段。

1. 類實例化

    Java程序中,類可以被顯式或者隱式地實例化。

    顯式實例化的4種途徑:

  • 使用new操作符
  • 使用反射,調用java.lang.Class或java.lang.reflect.Constructor的newInstance()方法
  • 使用現有對象的clone()方法
  • 使用反序列化手段,調用java.io.ObjectInputStream的readObject()方法

    隱式實例化的方法:

  • Java的每個命令行參數都會創建相應的String對象,並把它們組織成String數組作爲一個參數傳遞給程序入口main(String args[])方法
  • JVM裝載的每個類型,都會隱式地創建描述該類型的Class實例
  • 字符串常量對應一個String對象
  • 字符串操作符“+”的運算結果如果不是編譯時常量,會創建一個String對象

    無論採用哪種方式實例化對象,JVM創建對象的步驟如下:

    1) 在堆中爲對象分配內存

    2) 實例變量初始化爲默認值

    3) 對象初始化,給實例變量賦正確的初始值

 

       Java編譯器在編譯每個類時都會爲該類至少生成一個實例初始化方法——即“<init>()”方法。此方法與源代碼中的每個構造方法相對應。如果類沒有明確地聲明任何構造方法,編譯器則爲該類生成一個默認的無參構造方法,這個默認的構造器僅僅調用父類的無參構造器,與此同時也會生成一個與默認構造方法對應的“ <init>()”方法。

      <init>()方法內可能包括的代碼內容大概爲:調用另一個<init>()方法;對實例變量初始化;與其對應的構造方法內的代碼。
      如果構造方法是明確地從調用同一個類中的另一個構造方法(一個this()調用)開始,那它對應的<init>()方法體內包括的內容爲:一個同類的<init>()方法的調用;對應用構造方法內的所有字節碼。
      如果構造方法不是通過調用自身類的其它構造方法開始,並且該對象不是Object對象,那<init>()方法內則包括的內容爲:一個對父類<init>()方法的調用;對實例變量初始化方法的字節碼;最後是對應構造子的方法體字節碼。

      如果構造方法不是通過調用自身類的其它構造方法開始,並且這個類是Object,那麼它的<init>()方法則不包括對父類<init>()方法的調用。

      如果構造方法通過明確地調用超類的構造方法(一個super()調用)開始,那它對應的<init>()方法會調用對應的超類的<init>()方法。

      如果構造方法沒有明確地從this()或者super()調用開始,對應的<init>()方法默認會調用超類的無參數<init>()方法。

2. 對象使用

      Java中是通過引用來使用對象。在JDK 1.2以前的版本中,若一個對象不被任何變量引用,那麼程序就無法再使用這個對象。也就是說,只有對象處於可觸及狀態,程序才能使用它。從JDK 1.2版本開始,把對象的引用分爲4種級別,從而使程序能更加靈活地控制對象的生命週期。這4種級別由高到低依次爲:強引用、軟引用、弱引用和虛引用。其中,強引用是應用最普遍的引用。

      java.lang.ref.Reference類的三個直接子類:SoftReference、WeakReference和PhantomReference,分別對應着可觸及狀態的三個比較弱的形式:軟引用、弱引用和虛引用。強引用與較弱形式的引用的最基本區別是:強引用禁止引用目標被垃圾收集器回收,而較弱形式的引用不禁止。創建軟引用、弱引用和虛引用,只需要簡單地把強引用傳遞到對應的引用對象的構造方法中。下面介紹一下4種級別的引用的特點。

  • 強引用

     如果一個對象具有強引用,垃圾收集器絕不會回收它。當內存空間不足,JVM寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會考隨意回收具有強引用的對象來解決內存不足的問題。

  • 軟引用

     如果一個對象具有軟引用,當內存空間足夠時,垃圾收集器不會回收它;當內存不足時,就會回收這些對象的內存。只要垃圾收集器沒有回收它,該對象就可以被程序使用。因此,軟引用可用來實現內存敏感的高速緩存。

  • 弱引用

     如果一個對象具有弱引用,當垃圾收集器發現只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由於垃圾收集器是一個優先級很低的線程,因此不一定會很快發現只具有弱引用的對象。

  • 虛引用

     虛引用不會決定對象的生命週期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾收集器回收。虛引用主要用來跟蹤對象被垃圾回收的活動。

3. 垃圾收集

      程序可以顯式或者隱式地爲對象分配內存,但是不能明確地釋放內存。當一個對象不再被程序所引用時,JVM會通過垃圾收集器回收該對象的內存空間。

      如果類聲明一個返回值爲void的finalize()方法,垃圾收集器會在釋放這個類的實例對象所佔內存之前執行這個方法一次。由於finalize()方法是一個普通的Java方法,它可以直接被程序所調用。這樣的直接調用不會影響垃圾收集器的自動調用過程。垃圾收集器最多隻會調用一次對象的finalize()方法。如果finalize()方法執行後,對象重新被引用,隨後再次變得不被引用,垃圾收集器不會第二次調用finalize()方法。

     finalize()方法具有如下特點:

  • 垃圾收集器何時執行該方法是不確定的
  • finalize()方法可能使對象復活
  • 垃圾收集器自動調用finalize()方法拋出的任何異常都將被忽略

     在垃圾收集器看來,堆中的對象都處於三種狀態之一:

  • 可觸及:可以從根節點開始通過追蹤“觸及”到該對象;
  • 可復活:從根節點開始的追蹤圖中不可觸及,但是可能在垃圾收集器執行某些終結方法時觸及;
  • 不可觸及:從根節點開始的追蹤圖中不可觸及,同時不可能通過任何終結方法復活。垃圾收集器只回收該狀態下的對象。

    垃圾收集器的具體回收策略,可以看之前的博客:

Java虛擬機之垃圾回收 

發佈了34 篇原創文章 · 獲贊 22 · 訪問量 38萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章