1. 程式人生 > >.net 垃圾回收

.net 垃圾回收

垃圾回收器幫我們處理了記憶體中不在使用的物件,提高了機器的效能,讓開發人員輕鬆了很多。

你真的瞭解垃圾回收嗎?

或許你知道垃圾回收,聽說過是通過標記回收,可是怎麼標記回收呢就不是很清楚了,好吧,如果不清楚就繼續往下看。如果你是大神對這塊瞭如執掌,請直接跳過,歡迎來提不同的意見。

1、我們先來聊一下記憶體分配:

程式碼中宣告變數是需要向記憶體申請地址的,記憶體呢又分託管堆和棧,我們今天主要聊的就是託管堆記憶體

啥事託管堆記憶體呢?想必各位也心中知道,不知道的自行百度谷歌去。

寫程式碼中凡是需要使用new宣告的變數都是引用型別變數,使用的都是託管堆記憶體地址,那聲明瞭一個物件,需要分配多大的控制元件呢?

1.1、這個時候就需要計算型別的欄位需要的位元組數了

1.2、引用型別物件開銷的位元組數還需要(型別物件指標和同步索引塊)

  在32位應用中,這多出來的兩個欄位各需32位位元組地址空間,所以每個物件需要多佔用8個位元組的地址控制元件

  在64位應用中,這多出來的兩個欄位各需64位位元組地址空間,所以每個物件需要多佔用16個位元組的地址控制元件

1.3、記憶體申請後,CLR會檢查保留區是否能夠提供分配物件所需的位元組數,使用new 宣告的物件會向託管堆請求地址分配,並返回物件地址,NextObjPtr指標會加上物件佔據的位元組數,得到一個新值

2、垃圾回收-Go Go Go

垃圾回收的基本邏輯:垃圾回收器會檢查託管堆中是否又應用程式不再使用的任何物件,如果有,它們使用的記憶體就可以回收了。

回收之前的託管堆如下:

下面我們來聊一下標記回收的整個過程:

2.1、首先,應用有一組根(root)每個根都是一個儲存位置,其中包含指向引用型別物件的一個指標,指標要麼引用託管堆中的一個物件,要麼為null

  例如:型別中定義的任何靜態欄位被認為是一個根

       任何方法引數或區域性變數也被認為是一個根,只有引用型別的變數才被認為是一個根,值型別不能被認為是根。

2.2、垃圾回收的第一階段,標記階段:

  這時,垃圾回收器會沿著執行緒棧上行以檢查所有根,如果發現一個根引用了一個物件,就在物件 “同步索引塊”上開啟一位---標記,

  以遞迴的方式遍歷所有可達的物件。如果垃圾回收器試圖示記一個先前標記過的物件,就會停止沿這個路徑走下去。

    這個行為有兩個目的:

      1、垃圾回收器不會多次遍歷一個物件,所以效能得到顯著增強

      2、如果物件存在迴圈連結串列,可以避免無線迴圈。

   檢查完所有的根之後,堆中將包含一組已標記和未標記的物件,已標記的物件是程式碼可達的物件,而未標記的物件是不可達的,不可達的物件被認為是垃圾,它們佔用的記憶體是可以被回收的

垃圾回收之後的託管堆如下:

2.3、垃圾回收的第二階段,壓縮階段:

  這個時候該回收記憶體空間已經都回收了,空出來的記憶體可能是前頭一塊,中間一塊,後邊又一塊。

  垃圾回收器線性遍歷堆,以尋找未標記物件的連續記憶體塊,如果發現記憶體塊比較小,則忽略,如果發現大的,可用的連續記憶體塊,垃圾回收器會把非垃圾的物件移動到這裡以壓縮堆。

 

參考:CLR Via C#(第三版)