垃圾收集(Garbage Collection)機制
- 前言
- 1.GC需要回收哪些記憶體?
- 2.回收的基本單位?
- 1.標記:判斷物件生死
- (1)參考計數法(Python中使用,這里做簡單介紹)
- (2)可達性分析(Java中使用)
- 2.回收:把死的物件回識訓去
- (1)標記-清除演算法
- (2)標記-復制演算法
- (3)標記-整理演算法
前言
垃圾收集(Garbage Collection)簡稱GC,
1.GC需要回收哪些記憶體?
主要回收堆,其次是方法區,堆疊中不需要回收,執行緒結束,堆疊上的記憶體就會被釋放,
2.回收的基本單位?
記憶體的單位是“位元組”,但回收是按“物件”為單位進行的,
1.標記:判斷物件生死
垃圾收集器在對堆進行回收之前,第一件事就是要確定堆中的物件是否“存活”,因此我們需要用標記的方法來給物件打上“生死標簽”,
判斷物件生死有下面兩種方法:
(1)參考計數法(Python中使用,這里做簡單介紹)
眾所周知,創建一個物件,它必須被參考才會有意義,不然如何尋找這個物件呢?
所謂的參考計數法,就是在物件中添加一個參考計數器,每當有一個地方參考它時,計數器就加一;當參考失效時,計數器的值就減一;任何時刻計數器為零的物件就不可能再被使用了,此時該物件就會被標記,
優點:
原理簡單,判定效率高效,
缺點:
致命缺點,它無法解決物件之間相互參考的問題,
舉一個例子:
public class Test1 {
public Object o1 = null;
public static void testGC(){
//創建完成時兩個物件的參考計數器都為1
Test1 A = new Test1(); //假設A指向物件1
Test1 B = new Test1(); //假設B指向物件2
//兩個物件相互回圈參考(此時兩個物件的參考計數器都為2)
A.o1 = B;
B.o1 = A;
//讓兩個參考為空(此時計數器減一,兩個物件的參考計數器都為1)
A = null;
B = null;
}
}
此時我們再想使用物件1和物件2時已經不可能了:當我想找物件1的參考時,發現該參考在物件2中;當我想找物件2的參考時,發現該參考在物件1中,所以說這兩個物件已經廢了,但如果用參考計數法來標記的話,它們卻還是活著的物件,這就是參考計數法的最大缺陷,
(2)可達性分析(Java中使用)
? Java中是通過可達性分析(Reachability Analysis)演算法來判定物件是否存活,這個演算法的基本思路是通過一系列被稱為“GC Roots”的根物件作為起始節點集,從這些節點開始,根據參考關系向下搜索,搜索程序所走的程序稱為“參考鏈”,如果某個物件到“GC Roots”間沒有任何參考鏈相連,也就是不可達時,則證明此物件不可能再被使用,
![[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3J6k90M6-1636792466508)(C:\Users\15921\Pictures\JVM\可達性分析.jpg)]](https://img.uj5u.com/2021/11/14/284258141345101.png)
圖中物件object5、object6、object7都是不可達的,因此它們都將被判定為可回收的物件,
從可達性分析的定義中可以看到“GC Roots”這個根物件是一個節點集,因此可以作為根物件并不是只有一個節點,而是有一個集合,
在Java中固定可以作為GC Roots的物件包括但不限于以下幾種:
- 在方法區中類靜態屬性參考的變數,比如:Java類的參考型別靜態變數
- 在方法區中常量參考的物件,比如:字串常量池中的參考
- Native方法參考的物件
2.回收:把死的物件回識訓去
知道垃圾是誰了,那么接下來就是清理垃圾的程序,Java中的垃圾收集演算法有三種:標記-清除演算法、標記-復制演算法、標記-整理演算法,從名字我們就可以看出來標記的必要性了,
(1)標記-清除演算法
如它的名字一樣,該演算法分為兩個階段:“標記”和“清除”,首先標記處所有需要回收的物件,再統一回收被標記的物件;或者反過來,標記存活的物件,回收未被標記的物件,該演算法是最基礎的收集演算法,
優點:
效率高(但是缺點也和效率有關)
缺點:1.效率不穩定,試想一下如果我們在標記程序中,正好標記的是那些“大多數”,再往極限想,全部都要標記,那標記的程序就是一個極其耗時的程序,
2.記憶體空間的碎片化,當完成標記和清除操作后,記憶體空間會產生大量的不連續記憶體碎片(就像洗掉陣列中的元素卻不移動其他元素一樣),這就可能導致今后我們在給一個大物件分配空間時找不到一個足夠大的連續記憶體而不得不再出發另外一次垃圾收集,

(2)標記-復制演算法
也是“人”如其名,常被簡稱為復制演算法,是為了解決標記-清除演算法的缺點被提出的,它將可用的記憶體分為大小相等的兩份,每次只使用其中一塊,這一塊的記憶體用完了,就將存活的物件復制到另外一塊上,然后再把已使用的那一塊記憶體一次性清理掉,這樣就解決了標記-清除演算法的效率不穩定和記憶體空間碎片化的問題,
優點:
實作簡單,運行高效
缺點:嚴重浪費記憶體空間

(3)標記-整理演算法
所謂標記-整理演算法,就是標記-清除演算法的另外一個改進版本,顧名思義:標記完成后先將所有存活的物件往記憶體空間的一端移動,然后直接清除掉邊界以外的記憶體,
優點:
解決了記憶體空間碎片化的問題
缺點:移動存活的物件是一種極其負重的操作,而且想移動存活的物件必須先全程暫停用戶應用程式才能進行,像這種停頓被描述為"Stop The World",

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/356972.html
標籤:java
上一篇:File類及I/O流講解
下一篇:IO流(Java)
