標籤:
記憶體回收(Garbage Collection, GC)是Java不同於c與c++的重要特性之一。
他協助Java自動清空堆中不再使用的對象。
由於不需要手動釋放記憶體,程式員在編程中也可以減少犯錯的機會。
利用記憶體回收,程式員可以避免一些指標和記憶體泄露相關的bug(這一類bug通常很隱蔽)。
記憶體回收實際上是將原本屬於程式員的責任轉移給電腦。
GC需要完成的3件事情:
哪些記憶體需要回收
什麼時候回收
如何回收
1 回收那些對象?
在Java中採用可達性分析演算法來判定對象是否存活,是否可以被回收。
這個演算法通過一系列的被稱為”GC Root”的對象作為根節點,
從他們開始向下搜尋,搜尋走過的路徑被稱為引用鏈(Reference Chain).
當一個對象沒有一條引用鏈與GC Root 串連時,
即從GC Root 到這個對象是不可達的,說明這個對象是停用。
示:
object5 object6 object7 雖然互聯互連
但是他們到GC Root是不可達的
所以他們將被判定為可以回收的對象
那麼有一個重要的問題是 what is GC Root?
在Java語言中,可以看做是GC Root的是:
虛擬機器棧中引用的變數 (可理解為方法中的局部變數)
方法區中的類靜態屬性引用的對象
方法區中的常量引用的對象
本地方法棧中的JNI(native方法)引用的對象
2 記憶體回收演算法
2.1 標記-清除演算法
顧名思義,該方法分為標記和清除2個過程
標記:將所有需要回收的對象地區進行標記
清除:清除所有配標記的地區裡的對象
演算法不足之處:
效率問題:標記和清除的效率都不高
空間問題:清除後產生的空間是不連續的片段
無法滿足後續運行中大對象的需求
2.2 複製演算法
將整個空間劃分2個相等的地區,每次只使用其中一個地區
當一塊記憶體不夠時,就將活著的對象複製到另一塊記憶體
然後將第一塊的記憶體全部回收。
這樣每次對整個半區回收,就不會有記憶體片段的情況,實現簡單,運行高效
問題: 該演算法的代價就是可以記憶體大小縮小為原來的一半
解決:現在商用的虛擬機器都採取複製演算法.
但由於所有的對象是朝生夕死的,所以並不是按照1:1的比例來劃分記憶體的
而是將記憶體劃分為一塊較大的Eden區(new一個對象是就是在這裡面分配空間)
和2塊Survivor地區。每次使用Eden和一塊Survivor。
當回收的時候,將Eden和Survivor區中還存活的對象一次性賦值到另一塊Survivor中
最後清理掉原來使用過的Eden和Survivor區
這3個地區也被稱為新生代。HotSpot的預設新生代各地區比例如下:
每次新生代中可用的空間為整個新生代的90%, 即80% Eden + 10% 1個Survivor
此時只有10% 1個Survivor 會被’浪費’
如果另一塊Survivor地區存放不下Eden和Survivor區存活下來的對象
就要依靠其他地區來存放 即老年代
2.3 標記-整理演算法
類似於標記-清除演算法,先標記所有可以回收的地區,然後不是直接回收,
而是把所有存活的對象都移動到一端,然後直接清理掉端邊界以外的地區
標記-整理演算法和標記-清除演算法常用於老年代的回收
老年代存放的對象存活的時間較長
而且記憶體回收的頻率不如新生代的頻繁
Java GC 記憶體回收演算法 記憶體配置