Java 詳解垃圾回收與對象生命周期
Java 垃圾回收與對象生命周期詳解
Java中的垃圾回收與對象生命周期
1. 垃圾回收
垃圾回收是Java程序設(shè)計中內(nèi)存管理的核心概念,JVM的內(nèi)存管理機制被稱為垃圾回收機制。
一個對象創(chuàng)建后被放置在JVM的堆內(nèi)存中,當永遠不再引用這個對象時,它將被JVM在堆內(nèi)存中回收。被創(chuàng)建的對象不能再生,同時也沒有辦法通過程序語句釋放它們。即當對象在JVM運行空間中無法通過根集合到達(找到)時,這個對象被稱為垃圾對象。根集合是由類中的靜態(tài)引用域與本地引用域組成的。JVM通過根集合索引對象。
在做Java應(yīng)用開發(fā)時經(jīng)常會用到由JVM管理的兩種類型的內(nèi)存:堆內(nèi)存和棧內(nèi)存。簡單來講,堆內(nèi)存主要用來存儲程序在運行時創(chuàng)建或?qū)嵗膶ο笈c變量。例如通過new關(guān)鍵字創(chuàng)建的對象。而棧內(nèi)存則是用來存儲程序代碼中聲明為靜態(tài)或非靜態(tài)的方法。
(1) 堆內(nèi)存
堆內(nèi)存在JVM啟動的時候被創(chuàng)建,堆內(nèi)存中所存儲的對象可以被JVM自動回收,不能通過其他外部手段回收,也就是說開發(fā)人員無法通過添加相關(guān)代碼的手段來回收堆內(nèi)存中的對象。堆內(nèi)存通常情況下被分為兩個區(qū)域:新對象區(qū)域與老對象區(qū)域。
新對象區(qū)域:又可細分為三個小區(qū)域:伊甸園區(qū)域、From區(qū)域與To區(qū)域。伊甸園區(qū)域用來保存新創(chuàng)建的對象,它就像一個堆棧,新的對象被創(chuàng)建,就像指向該棧的指針在增長一樣,當伊甸園區(qū)域中的對象滿了之后,JVM系統(tǒng)將要做到可達性測試,主要任務(wù)是檢測有哪些對象由根集合出發(fā)是不可達的,這些對象就可以被JVM回收,并且將所有的活動對象從伊甸園區(qū)域拷貝到To區(qū)域,此時一些對象將發(fā)生狀態(tài)交換,有的對象就從To區(qū)域被轉(zhuǎn)移到From區(qū)域,此時From區(qū)域就有了對象。上面對象遷移的整個過程,都是由JVM控制完成的。
老對象區(qū)域:在老對象區(qū)域中的對象仍然會有一個較長的生命周期,大多數(shù)的JVM系統(tǒng)垃圾對象,都是源于"短命"對象,經(jīng)過一段時間后,被轉(zhuǎn)入老對象區(qū)域的對象,就變成了垃圾對象。此時,它們都被打上相應(yīng)的標記,JVM系統(tǒng)將會自動回收這些垃圾對象,建議不要頻繁地強制系統(tǒng)作垃圾回收,這是因為JVM會利用有限的系統(tǒng)資源,優(yōu)先完成垃圾回收工作,導(dǎo)致應(yīng)用無法快速地響應(yīng)來自用戶端的請求,這樣會影響系統(tǒng)的整體性能。
(2) 棧內(nèi)存
堆內(nèi)存主要用來存儲程序在運行時創(chuàng)建或?qū)嵗膶ο笈c變量。例如通過new關(guān)鍵字創(chuàng)建的對象。而棧內(nèi)存則是用來存儲程序代碼中聲明為靜態(tài)或非靜態(tài)的方法。
2. JVM中對象的生命周期
在JVM運行空間中,對象的整個生命周期大致可以分為7個階段:
- 創(chuàng)建階段;
- 應(yīng)用階段;
- 不可視階段;
- 不可到達階段;
- 可收集階段;
- 終結(jié)階段;
- 釋放階段
上面這7個階段,構(gòu)成了JVM中對象的完整的生命周期。
(1) 創(chuàng)建階段
在對象的創(chuàng)建階段,系統(tǒng)主要通過下面的步驟,完成對象的創(chuàng)建過程:
<1> 為對象分配存儲空間;
<2> 開始構(gòu)造對象;
<3> 從超類到子類對static成員進行初始化;
<4> 超類成員變量按順序初始化,遞歸調(diào)用超類的構(gòu)造方法;
<5> 子類成員變量按順序初始化,子類構(gòu)造方法調(diào)用。
在創(chuàng)建對象時應(yīng)注意幾個關(guān)鍵應(yīng)用規(guī)則:
<1> 避免在循環(huán)體中創(chuàng)建對象,即使該對象占用內(nèi)存空間不大。
<2> 盡量及時使對象符合垃圾回收標準。比如 myObject = null。
<3> 不要采用過深的繼承層次。
<4> 訪問本地變量優(yōu)于訪問類中的變量。
(2) 應(yīng)用階段
在對象的引用階段,對象具備如下特征:
<1> 系統(tǒng)至少維護著對象的一個強引用(Strong Reference);
<2> 所有對該對象的引用全部是強引用(除非我們顯示地適用了:軟引用(Soft Reference)、弱引用(Weak Reference)或虛引用(Phantom Reference)).
強引用(Strong Reference):是指JVM內(nèi)存管理器從根引用集合出發(fā)遍歷堆中所有到達對象的路徑。當?shù)竭_某對象的任意路徑都不含有引用對象時,這個對象的引用就被稱為強引用。
軟引用(Soft Reference):軟引用的主要特點是有較強的引用功能。只有當內(nèi)存不夠的時候,才回收這類內(nèi)存,因此內(nèi)存足夠時它們通常不被回收。另外這些引用對象還能保證在Java拋出OutOfMemory異常之前,被設(shè)置為null。它可以用于實現(xiàn)一些常用資源的緩存,實現(xiàn)Cache功能,保證最大限度地使用內(nèi)存你而不引起OutOfMemory。
下面是軟引用的實現(xiàn)代碼:
import java.lang.ref.SoftReference; ... A a = new A(); ... // 使用a ... // 使用完了a, 將它設(shè)置為soft引用類型,并且釋放強引用 SoftReference sr = new SoftReference(a); a = null; ... // 下次使用時 if (sr != null) { a = sr.get(); } else { // GC由于低內(nèi)存,已釋放a,因此需要重新裝載 a = new A(); sr = new SoftReference(a); }
軟引用技術(shù)的引進使Java應(yīng)用可以更好地管理內(nèi)存,穩(wěn)定系統(tǒng),防止系統(tǒng)內(nèi)存溢出,避免系統(tǒng)崩潰。因此在處理一些占用內(nèi)存較大且生命周期較長,但使用并不繁地對象時應(yīng)盡量應(yīng)用該技術(shù)。提高系統(tǒng)穩(wěn)定性。
弱引用(Weak Reference):弱應(yīng)用對象與軟引用對象的最大不同就在于:GC在進行垃圾回收時,需要通過算法檢查是否回收Soft應(yīng)用對象,而對于Weak引用,GC總是進行回收。Weak引用對象更容易、更快地被GC回收。Weak引用對象常常用于Map結(jié)構(gòu)中。
import java.lang.ref.WeakReference; ... A a = new A(); ... // 使用a ... // 使用完了a, 將它設(shè)置為Weak引用類型,并且釋放強引用 WeakReference wr = new WeakReference(a); a = null; ... // 下次使用時 if (wr != null) { a = wr.get(); } else { a = new A(); wr = new WeakReference(a); }
虛引用(Phantom Reference): 虛引用的用途較少,主要用于輔助finalize函數(shù)的使用。
虛引用(Phantom Reference)對象指一些執(zhí)行完了finalize函數(shù),并為不可達對象,但是還沒有被GC回收的對象。這種對象可以輔助finalize進行一些后期的回收工作,我們通過覆蓋了Refernce的clear()方法,增強資源回收機制的靈活性。
在實際程序設(shè)計中一般很少使用弱引用和虛引用,是用軟引用的情況較多,因為軟引用可以加速JVM對垃圾內(nèi)存的回收速度,可以維護系統(tǒng)的運行安全,防止內(nèi)存溢出(OutOfMemory)等問題的產(chǎn)生。
(3) 不可視階段
當一個對象處于不可視階段,說明我們在其他區(qū)域的代碼中已經(jīng)不可以在引用它,其強引用已經(jīng)消失,例如,本地變量超出了其可視
的范圍。
try { Object localObj = new Object(); localObj.doSomething(); } catch (Exception e) { e.printStackTrace(); } if (true) { // 此區(qū)域中l(wèi)ocalObj 對象已經(jīng)不可視了, 編譯器會報錯。 localObj.doSomething(); }
(4) 不可到達階段
處于不可達階段的對象在虛擬機的對象引用根集合中再也找不到直接或間接地強引用,這些對象一般是所有線程棧中的臨時變量。所有已經(jīng)裝載的靜態(tài)變量或者是對本地代碼接口的引用。
(5) 可收集階段、終結(jié)階段與釋放階段
當一個對象處于可收集階段、終結(jié)階段與釋放階段時,該對象有如下三種情況:
<1> 回收器發(fā)現(xiàn)該對象已經(jīng)不可達。
<2> finalize方法已經(jīng)被執(zhí)行。
<3> 對象空間已被重用。
感謝閱讀,希望能幫助到大家,謝謝大家對本站的支持!
相關(guān)文章
Java 根據(jù)某個 key 加鎖的實現(xiàn)方式
日常開發(fā)中,有時候需要根據(jù)某個 key 加鎖,確保多線程情況下,對該 key 的加鎖和解鎖之間的代碼串行執(zhí)行,這篇文章主要介紹了Java 根據(jù)某個 key 加鎖的實現(xiàn)方式,需要的朋友可以參考下2023-03-03將Swagger2文檔導(dǎo)出為HTML或markdown等格式離線閱讀解析
這篇文章主要介紹了將Swagger2文檔導(dǎo)出為HTML或markdown等格式離線閱讀,本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-11-11Spring使用AspectJ注解和XML配置實現(xiàn)AOP
這篇文章主要介紹了Spring使用AspectJ注解和XML配置實現(xiàn)AOP的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10springboot+spring?data?jpa實現(xiàn)新增及批量新增方式
這篇文章主要介紹了springboot+spring?data?jpa實現(xiàn)新增及批量新增方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11