GC(垃圾回收)


GC(垃圾回收)

​ GC,Garbage Collection,是JVM的重要組成部分,短短一篇博客只是管中窺豹,如果想深入學習GC和JVM,建議研讀周志明老師的的《深入理解Java虛擬機: JVM高級特性與最佳實踐》

1.GC

​ GC,Garbage Collection。是Java中的垃圾回收器。眾所周知,Java是借鑒的C++,它擯棄了C++中一些繁瑣容易出錯的東西。其中有一條就是這個GC。在C/C++程序中,程序員在內存中主動開辟一段相應的空間來存值。由於內存是有限的,所以當程序不再需要使用該內存空間時,就需要銷毀對象並釋放其所占用的內存資源,好重新利用這段空間。在C/C++中,釋放無用內存空間的事情需要由程序員自己來處理。就是說當程序員認為空間沒用了,就手動地釋放其占用的內存。但是這樣顯然非常繁瑣,如果有所遺漏,就可能造成資源浪費甚至內存泄露。當軟件系統比較復雜,變量多的時候程序員往往就忘記釋放內存或者在不該釋放的時候釋放內存了。

​ 有了GC,程序員就不需要再手動的去控制內存的釋放。當Java虛擬機發覺內存資源緊張的時候,就會自動地去清理無用對象(沒有被引用到的對象)所占用的內存空間(這里的說法略顯粗略,事實上何時清理內存是個復雜的策略)。如果需要,可以在程序中顯式地使用System.gc() / System.GC.Collect()來強制進行一次立即的內存清理。Java提供的GC功能可以自動監測對象是否超過了作用域,從而達到自動回收內存的目的,Java的GC會自動進行管理,調用方法:System.gc() 或者Runtime.getRuntime().gc();

2.需要GC的內存區域

​ 在JVM中,棧、本地方法棧、程序計數器生命周期都是和線程同步(隨線程生,隨線程滅)。我們的垃圾回收主要集中於Java的堆和方法區中,其中幾乎所有的GC都在堆中。

3.判斷GC對象的方法

​ 需要進行回收的對象是已經沒有存活的對象,判斷一個對象是否存活有兩種方法:引用計數和可達性分析。

1.引用計數

​ 每個對象都有一個引用計數屬性,新增一個引用時計數加1,釋放一個引用是計數減1,當計數為0時可以回收。此方法比較簡單,並且可以解決對象循環引用問題。

2.可達性分析(Reachability Analysis)

​ 從GC Roots開始向下搜索,搜索所走過的路徑稱為引用鏈。當一個對象到GC Roots沒有任何引用鏈相連時,則證明此對象是不可用的,是不可達對象。

​ 在Java語言中,GCRoot包括:

​ 虛擬機棧中引用的對象

​ 方法區中類靜態屬性實體引用的對象

​ 方法區中常量引用的對象

​ 本地方法棧中JNI引用的對象

4.GC分類和觸發

1.GC分類

​ GC分為輕GC和重GC。

​ 輕GC又被稱為minor GC、普通GC;

​ 重GC又被稱為Major GC、Full GC和全局GC;

2.GC觸發時間

​ 輕GC觸發條件:當堆里面的伊甸園區(Eden Area)內存空間不足。

​ 重GC觸發條件:老年代空間不足;方法區空間不足;幸存區空間不足;調用System.gc方法時。

5.GC常見算法

​ GC常見算法:引用計數法、復制算法、標記-清除法、標記壓縮法、標記-清除-壓縮法、分代收集算法。

​ 目前主流JVM(HotSpot)采用的是分代收集算法。

1.引用計數法

​ 引用計數算法很簡單,它實際上是通過在對象頭中分配一個空間來保存該對象被引用的次數。如果該對象被其它對象引用,則它的引用計數加1,如果刪除對該對象的引用,那么它的引用計數就減1,當該對象的引用計數為0時,那么該對象就會被回收。

​ 引用計數法跟其他垃圾回收算法不同,其他垃圾回收都是在為新對象分配內存空間時由於內存空間不足而觸發的,而且垃圾回收是針對整個堆中的所有對象進行的。而引用計數垃圾回收機制不一樣,它只是在引用計數變化為0時即刻發生,而且只針對某一個對象以及它所依賴的其它對象。所以,我們一般也稱呼引用計數垃圾收集為直接的垃圾收集機制,而前面三種都屬於間接的垃圾收集機制。

​ 優點:簡單

​ 缺點:計數器本身也有內存消耗;無法處理環形數據數據問題。

環形數據問題

​ 即如果有兩個對象相互引用,那么這兩個對象就不能被回收,因為它們的引用計數始終為1。這也就是我們常說的“內存泄漏”問題。比如下圖展示的將p變量賦值為null值后所出現的內存泄漏。

2.復制算法

​ 堆內存中的新生代中的伊甸園區內存不足的時候,就會觸發輕GC,存活下來的對象會存入幸存區。幸存區分為幸存0區和幸存1區,也可以稱為幸存from區和幸存to區,這兩個區域是動態變化的。(記住:幸存to區一定是空的)

​ 當回收時,會將伊甸園區幸存下來的對象和幸存區存活的對象(幸存from區)都復制幸存to區,然后原來的幸存from區變成了現在的幸存to區(幸存to區一定是空的),原來的幸存to區變成了現在的幸存from區。

​ 優點:沒有內存碎片

​ 缺點:浪費了幸存區一半內存空間

​ 假設對象100%存活的話(極端情況)--》OOM,所以復制算法比較適合對象存貨度比較低的區域,也就是新生區。

3.標記-清除算法

​ 顧名思義,標記-清除算法分為兩個階段,標記(mark)和清除(sweep)。每個對象都存儲一個標記位,標記階段,進行可達性分析,將對象標記為存活和死亡兩種狀態;在清除階段,從頭到尾進行線性的遍歷,將死亡的對象進行清除。

​ 優點:不需要額外空間;不改變對象位置。

​ 缺點:會產生內存碎片;兩次掃描浪費時間。

4.標記-壓縮算法

​ 標記-壓縮法是標記-清除算法的一個改進版。在標記階段,該算法也會將存活的對象標記為存活和死亡兩種狀態;不同的是,在第二個階段,該算法並沒有直接對死亡的對象進行清理,而是將所有存活的對象整理一下,放到另一處空間,然后把剩下的所有對象全部清除。

​ 優點:不會產生大量碎片空間;

​ 缺點:存活對象過多會導致效率降低;改變對象位置;

5.標記-清除-壓縮算法

​ 使用幾次標記清除之后會產生大量碎片空間,然后使用標記壓縮算法去除碎片空間。

6.分代收集算法

​ 現在的虛擬機垃圾收集大多采用這種方式,它根據對象的生存周期,將堆分為新生代和老年代。在新生代中,由於對象生存期短,每次回收都會有大量對象死去,那么這時就采用復制算法。老年代里的對象存活率較高,沒有額外的空間進行分配,所以可以使用標記-整理和標記-清除結合使用。

6.參考文獻

1.參考文獻

感謝:遇見狂神說

Java GC機制詳解:https://blog.csdn.net/laomo_bible/article/details/83112622

引用計數法:https://www.jianshu.com/p/1d5fa7f6035c

復制算法:https://www.jianshu.com/p/74659de07264

標記-清除算法:https://www.jianshu.com/p/b0f5d21fe031


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM