熱點推薦:
您现在的位置: 電腦知識網 >> 編程 >> Java編程 >> Java核心技術 >> 正文

詳細解讀JVM中的對象生命周期

2013-11-23 19:11:37  來源: Java核心技術 

  在JVM運行空間中對象的整個生命周期大致可以分為個階段創建階段(Creation)應用階段(Using)不可視階段(Invisible)不可到達階段(Unreachable)可收集階段(Collected)終結階段(Finalized)與釋放階段(Free)上面的這個階段構成了 JVM中對象的完整的生命周期下面分別介紹對象在處於這個階段時的不同情形

     創建階段

  在對象創建階段系統要通過下面的步驟完成對象的創建過程

  ()為對象分配存儲空間

  ()開始構造對象

  ()遞歸調用其超類的構造方法

  ()進行對象實例初始化與變量初始化

  ()執行構造方法體

  上面的個步驟中的第步就是指遞歸地調用該類所擴展的所有父類的構造方法一個Java類(除Object類外)至少有一個父類(Object)這個規則既是強制的也是隱式的你可能已經注意到在創建一個Java類的時候並沒有顯式地聲明擴展(extends)一個Object父類實際上在 Java程序設計中任何一個Java類都直接或間接的是Object類的子類例如下面的代碼

  public class A {      … } 這個聲明等同於下面的聲明 public class A extends javalangObject {      … }

  上面講解了對象處於創建階段時系統所做的一些處理工作其中有些過程與應用的性能密切相關因此在創建對象時我們應該遵循一些基本的規則以提高應用的性能

  下面是在創建對象時的幾個關鍵應用規則

  ()避免在循環體中創建對象即使該對象占用內存空間不大

  ()盡量及時使對象符合垃圾回收標准

  ()不要采用過深的繼承層次

  ()訪問本地變量優於訪問類中的變量

  關於規則()避免在循環體中創建對象即使該對象占用內存空間不大需要提示一下這種情況在我們的實際應用中經常遇到而且我們很容易犯類似的錯誤例如下面的代碼

  … … for (int i = ; i < ; ++i) {     Object obj = new Object();     Systemoutprintln(obj= + obj); } … …

  上面代碼的書寫方式相信對你來說不會陌生也許在以前的應用開發中你也這樣做過尤其是在枚舉一個Vector對象中的對象元素的操作中經常會這樣書寫但這卻違反了上述規則(因為這樣會浪費較大的內存空間正確的方法如下所示

  … … Object obj = null; for (int i = ; i < ; ++i) {     obj = new Object();     Systemoutprintln(obj= + obj); } … …

  采用上面的第二種編寫方式僅在內存中保存一份對該對象的引用而不像上面的第一種編寫方式中代碼會在內存中產生大量的對象應用浪費大量的內存空間而且增大了系統做垃圾回收的負荷因此在循環體中聲明創建對象的編寫方式應該盡量避免

  另外不要對一個對象進行多次初始化這同樣會帶來較大的內存開銷降低系統性能

  public class A { private Hashtable table = new Hashtable (); public A() { // 將Hashtable對象table初始化了兩次 table = new Hashtable(); } }

  正確的方式為

  public class B { private Hashtable table = new Hashtable (); public B() { } }

  不要小看這個差別它卻使應用軟件的性能相差甚遠如圖所示

  

  圖  初始化對象多次所帶來的性能差別

  看來在程序設計中也應該遵從勿以惡小而為之的古訓否則我們開發出來的應用也是低效的應用有時應用軟件中的一個極小的失誤就會大幅度地降低整個系統的性能因此我們在日常的應用開發中應該認真對待每一行代碼采用最優化的編寫方式不要忽視細節不要忽視潛在的問題

     應用階段

  當對象的創建階段結束之後該對象通常就會進入對象的應用階段這個階段是對象得以表現自身能力的階段也就是說對象的應用階段是對象整個生命周期中證明自身存在價值的時期在對象的應用階段對象具備下列特征

  — 系統至少維護著對象的一個強引用(Strong Reference)

  — 所有對該對象的引用全部是強引用(除非我們顯式地使用了軟引用(Soft Reference)弱引用(Weak Reference)或虛引用(Phantom Reference))

  上面提到了幾種不同的引用類型可能一些讀者對這幾種引用的概念還不是很清楚下面分別對之加以介紹在講解這幾種不同類型的引用之前我們必須先了解一下Java中對象引用的結構層次

  Java對象引用的結構層次示意如圖所示

  

  圖  對象引用的結構層次示意

  由圖我們不難看出上面所提到的幾種引用的層次關系其中強引用處於頂端而虛引用則處於底端下面分別予以介紹

  .強引用

  強引用(Strong Reference)是指JVM內存管理器從根引用集合(Root Set)出發遍尋堆中所有到達對象的路徑當到達某對象的任意路徑都不含有引用對象時對這個對象的引用就被稱為強引用

  .軟引用

  軟引用(Soft Reference)的主要特點是具有較強的引用功能只有當內存不夠的時候才回收這類內存因此在內存足夠的時候它們通常不被回收另外這些引用對象還能保證在Java拋出OutOfMemory 異常之前被設置為null它可以用於實現一些常用資源的緩存實現Cache的功能保證最大限度的使用內存而不引起OutOfMemory再者軟可到達對象的所有軟引用都要保證在虛擬機拋出OutOfMemoryError之前已經被清除否則清除軟引用的時間或者清除不同對象的一組此類引用的順序將不受任何約束然而虛擬機實現不鼓勵清除最近訪問或使用過的軟引用下面是軟引用的實現代碼

  

  … … import javalangrefSoftReference; … A a = new A(); … // 使用 a … // 使用完了a將它設置為soft 引用類型並且釋放強引用 SoftReference sr = new SoftReference(a); a = null; … // 下次使用時 if (sr!=null) { a = srget(); } else{ // GC由於內存資源不足可能系統已回收了a的軟引用 // 因此需要重新裝載 a = new A(); sr=new SoftReference(a); } … …

  軟引用技術的引進使Java應用可以更好地管理內存穩定系統防止系統內存溢出避免系統崩潰(crash)因此在處理一些占用內存較大而且聲明周期較長但使用並不頻繁的對象時應盡量應用該技術正像上面的代碼一樣我們可以在對象被回收之後重新創建(這裡是指那些沒有保留運行過程中狀態的對象)提高應用對內存的使用效率提高系統穩定性但事物總是帶有兩面性的有利亦有弊在某些時候對軟引用的使用會降低應用的運行效率與性能例如應用軟引用的對象的初始化過程較為耗時或者對象的狀態在程序的運行過程中發生了變化都會給重新創建對象與初始化對象帶來不同程度的麻煩有些時候我們要權衡利弊擇時應用

  .弱引用

  弱引用(Weak Reference)對象與Soft引用對象的最大不同就在於GC在進行回收時需要通過算法檢查是否回收Soft引用對象而對於Weak引用對象 GC總是進行回收因此Weak引用對象會更容易更快被GC回收雖然GC在運行時一定回收Weak引用對象但是復雜關系的Weak對象群常常需要好幾次GC的運行才能完成Weak引用對象常常用於Map數據結構中引用占用內存空間較大的對象一旦該對象的強引用為null時對這個對象引用就不存在了GC能夠快速地回收該對象空間與軟引用類似我們也可以給出相應的應用代碼

  

  … … import javalangrefWeakReference; … A a = new A(); … // 使用 a … // 使用完了a將它設置為weak 引用類型並且釋放強引用 WeakReference wr = new WeakReference (a); a = null; … // 下次使用時 if (wr!=null) { a = wrget(); } else{ a = new A(); wr = new WeakReference (a); } … …

  弱引用技術主要適用於實現無法防止其鍵(或值)被回收的規范化映射另外弱引用分為短弱引用(Short Week Reference)長弱引用(Long Week Reference)其區別是長弱引用在對象的Finalize方法被GC調用後依然追蹤對象基於安全考慮不推薦使用長弱引用因此建議使用下面的方式創建對象的弱引用

  

  … … WeakReference wr = new WeakReference(obj); 或 WeakReference wr = new WeakReference(obj false); … …

  .虛引用

  虛引用(Phantom Reference)的用途較少主要用於輔助finalize函數的使用Phantom對象指一些執行完了finalize函數並且為不可達對象但是還沒有被GC回收的對象這種對象可以輔助finalize進行一些後期的回收工作我們通過覆蓋Reference的clear()方法增強資源回收機制的靈活性虛引用主要適用於以某種比 java 終結機制更靈活的方式調度 premortem 清除操作

  &注意  在實際程序設計中一般很少使用弱引用與虛引用使用軟引用的情況較多這是因為軟引用可以加速JVM對垃圾內存的回收速度可以維護系統的運行安全防止內存溢出(OutOfMemory)等問題的產生

     不可視階段

  在一個對象經歷了應用階段之後那麼該對象便處於不可視階段說明我們在其他區域的代碼中已經不可以再引用它其強引用已經消失例如本地變量超出了其可視范圍如下所示

  

  … … public void process () { try { Object obj = new Object(); objdoSomething(); } catch (Exception e) { eprintStackTrace(); } while (isLoop) { // loops forever // 這個區域對於obj對象來說已經是不可視的了 // 因此下面的代碼在編譯時會引發錯誤 objdoSomething(); } } … …

  如果一個對象已使用完而且在其可視區域不再使用此時應該主動將其設置為空(null)可以在上面的代碼行objdoSomething();下添加代碼行obj = null;這樣一行代碼強制將obj對象置為空值這樣做的意義是可以幫助JVM及時地發現這個垃圾對象並且可以及時地回收該對象所占用的系統資源

     不可到達階段

  處於不可到達階段的對象在虛擬機所管理的對象引用根集合中再也找不到直接或間接的強引用這些對象通常是指所有線程棧中的臨時變量所有已裝載的類的靜態變量或者對本地代碼接口(JNI)的引用這些對象都是要被垃圾回收器回收的預備對象但此時該對象並不能被垃圾回收器直接回收其實所有垃圾回收算法所面臨的問題是相同的——找出由分配器分配的但是用戶程序不可到達的內存塊

      可收集階段終結階段與釋放階段

  對象生命周期的最後一個階段是可收集階段終結階段與釋放階段當對象處於這個階段的時候可能處於下面三種情況

  ()垃圾回收器發現該對象已經不可到達

  ()finalize方法已經被執行

  ()對象空間已被重用

  當對象處於上面的三種情況時該對象就處於可收集階段終結階段與釋放階段了虛擬機就可以直接將該對象回收了


From:http://tw.wingwit.com/Article/program/Java/hx/201311/26415.html
    推薦文章
    Copyright © 2005-2013 電腦知識網 Computer Knowledge   All rights reserved.