1. 垃圾回收
垃圾回收是Java程序設計中內存管理的核心概念,JVM的內存管理機制被稱為垃圾回收機制。
一個對象創建后被放置在JVM的堆內存中,當永遠不再引用這個對象時,它將被JVM在堆內存中回收。被創建的對象不能再生,同時也沒有辦法通過程序語句釋放它們。即當對象在JVM運行空間中無法通過根集合到達(找到)時,這個對象被稱為垃圾對象。根集合是由類中的靜態引用域與本地引用域組成的。JVM通過根集合索引對象。
在做Java應用開發時經常會用到由JVM管理的兩種類型的內存:堆內存和棧內存。簡單來講,堆內存主要用來存儲程序在運行時創建或實例化的對象與變量。例如通過new關鍵字創建的對象。而棧內存則是用來存儲程序代碼中聲明為靜態或非靜態的方法。
(1) 堆內存
堆內存在JVM啟動的時候被創建,堆內存中所存儲的對象可以被JVM自動回收,不能通過其他外部手段回收,也就是說開發人員無法通過添加相關代碼的手段來回收堆內存中的對象。堆內存通常情況下被分為兩個區域:新對象區域與老對象區域。
新對象區域:又可細分為三個小區域:伊甸園區域、From區域與To區域。伊甸園區域用來保存新創建的對象,它就像一個堆棧,新的對象被創建,就像指向該棧的指針在增長一樣,當伊甸園區域中的對象滿了之后,JVM系統將要做到可達性測試,主要任務是檢測有哪些對象由根集合出發是不可達的,這些對象就可以被JVM回收,並且將所有的活動對象從伊甸園區域拷貝到To區域,此時一些對象將發生狀態交換,有的對象就從To區域被轉移到From區域,此時From區域就有了對象。上面對象遷移的整個過程,都是由JVM控制完成的。
老對象區域:在老對象區域中的對象仍然會有一個較長的生命周期,大多數的JVM系統垃圾對象,都是源於"短命"對象,經過一段時間后,被轉入老對象區域的對象,就變成了垃圾對象。此時,它們都被打上相應的標記,JVM系統將會自動回收這些垃圾對象,建議不要頻繁地強制系統作垃圾回收,這是因為JVM會利用有限的系統資源,優先完成垃圾回收工作,導致應用無法快速地響應來自用戶端的請求,這樣會影響系統的整體性能。
(2) 棧內存
堆內存主要用來存儲程序在運行時創建或實例化的對象與變量。例如通過new關鍵字創建的對象。而棧內存則是用來存儲程序代碼中聲明為靜態或非靜態的方法。
2. JVM中對象的生命周期
在JVM運行空間中,對象的整個生命周期大致可以分為7個階段:
創建階段;
應用階段;
不可視階段;
不可到達階段;
可收集階段;
終結階段;
釋放階段
上面這7個階段,構成了JVM中對象的完整的生命周期。
(1) 創建階段
在對象的創建階段,系統主要通過下面的步驟,完成對象的創建過程:
<1> 為對象分配存儲空間;
<2> 開始構造對象;
<3> 從超類到子類對static成員進行初始化;
<4> 超類成員變量按順序初始化,遞歸調用超類的構造方法;
<5> 子類成員變量按順序初始化,子類構造方法調用。
在創建對象時應注意幾個關鍵應用規則:
<1> 避免在循環體中創建對象,即使該對象占用內存空間不大。
<2> 盡量及時使對象符合垃圾回收標准。比如 myObject = null。
<3> 不要采用過深的繼承層次。
<4> 訪問本地變量優於訪問類中的變量。
(2) 應用階段
在對象的引用階段,對象具備如下特征:
<1> 系統至少維護着對象的一個強引用(Strong Reference);
<2> 所有對該對象的引用全部是強引用(除非我們顯示地適用了:軟引用(Soft Reference)、弱引用(Weak Reference)或虛引用(Phantom Reference)).
強引用(Strong Reference):是指JVM內存管理器從根引用集合出發遍歷堆中所有到達對象的路徑。當到達某對象的任意路徑都不含有引用對象時,這個對象的引用就被稱為強引用。
軟引用(Soft Reference):軟引用的主要特點是有較強的引用功能。只有當內存不夠的時候,才回收這類內存,因此內存足夠時它們通常不被回收。另外這些引用對象還能保證在Java拋出OutOfMemory異常之前,被設置為null。它可以用於實現一些常用資源的緩存,實現Cache功能,保證最大限度地使用內存你而不引起OutOfMemory。
下面是軟引用的實現代碼: import java.lang.ref.SoftReference; ... A a = new A(); ... // 使用a ... // 使用完了a, 將它設置為soft引用類型,並且釋放強引用 SoftReference sr = new SoftReference(a); a = null; ... // 下次使用時 if (sr != null) { a = sr.get(); } else { // GC由於低內存,已釋放a,因此需要重新裝載 a = new A(); sr = new SoftReference(a); }
軟引用技術的引進使Java應用可以更好地管理內存,穩定系統,防止系統內存溢出,避免系統崩潰。因此在處理一些占用內存較大且生命周期較長,但使用並不繁地對象時應盡量應用該技術。提高系統穩定性。
弱引用(Weak Reference):弱應用對象與軟引用對象的最大不同就在於:GC在進行垃圾回收時,需要通過算法檢查是否回收Soft應用對象,而對於Weak引用,GC總是進行回收。Weak引用對象更容易、更快地被GC回收。Weak引用對象常常用於Map結構中。
import java.lang.ref.WeakReference; 4. ... 5. 6. A a = new A(); 7. ... 8. 9. // 使用a 10. ... 11. 12. // 使用完了a, 將它設置為Weak引用類型,並且釋放強引用 13. WeakReference wr = new WeakReference(a); 14. a = null; 15. ... 16. 17. // 下次使用時 18. if (wr != null) { 19. a = wr.get(); 20. } else { 21. a = new A(); 22. wr = new WeakReference(a); 23. }
虛引用(Phantom Reference): 虛引用的用途較少,主要用於輔助finalize函數的使用。
虛引用(Phantom Reference)對象指一些執行完了finalize函數,並為不可達對象,但是還沒有被GC回收的對象。這種對象可以輔助finalize進行一些后期的回收工作,我們通過覆蓋了Refernce的clear()方法,增強資源回收機制的靈活性。
在實際程序設計中一般很少使用弱引用和虛引用,是用軟引用的情況較多,因為軟引用可以加速JVM對垃圾內存的回收速度,可以維護系統的運行安全,防止內存溢出(OutOfMemory)等問題的產生。
(3) 不可視階段
當一個對象處於不可視階段,說明我們在其他區域的代碼中已經不可以在引用它,其強引用已經消失,例如,本地變量超出了其可視
的范圍。
1.try { 2. Object localObj = new Object(); 3. localObj.doSomething(); 4. } catch (Exception e) { 5. e.printStackTrace(); 6. } 7. 8. if (true) { 9. // 此區域中localObj 對象已經不可視了, 編譯器會報錯。 10. localObj.doSomething(); 11. }
(4) 不可到達階段
處於不可達階段的對象在虛擬機的對象引用根集合中再也找不到直接或間接地強引用,這些對象一般是所有線程棧中的臨時變量。所有已經裝載的靜態變量或者是對本地代碼接口的引用。
(5) 可收集階段、終結階段與釋放階段
當一個對象處於可收集階段、終結階段與釋放階段時,該對象有如下三種情況:
<1> 回收器發現該對象已經不可達。
<2> finalize方法已經被執行。
<3> 對象空間已被重用。