天天看點

.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#(第三版)

原文位址

https://www.cnblogs.com/miao817/p/12705140.html