C#記憶體管理與記憶體回收

來源:互聯網
上載者:User

記憶體回收還得從根說起,就像生兒育女一樣。

:根是一個位置,存放一個指標,該指標指向託管堆中的一個對象,或是一個null 指標不指向任何對象,即為null。根存線上程棧或託管堆中,大部分的跟都線上程棧上,因為定義的變數就存線上程棧上,類型對象指標存在託管堆中,因為執行個體化一個對象要額外分配兩個欄位“類型對象指標”和“同步塊索引”。

 

類型對象指標的作用。執行個體化一個對象並沒有為其方法分配記憶體,類型的靜態欄位分配記憶體,而執行個體要向調用屬於類型的一些東西,就必須通過類型對象指標。如對象的執行個體是共用類型的方法,執行個體只需要通過類型對象指標調用類型的方法,更多關於方法的調用請看我的這篇部落格。

 

同步塊索引的作用。1:用於lock,使對象在同一時刻只能一個線程訪問;2:用於擷取對象的hashCode;3:在記憶體回收時標誌某個對象是否是垃圾。關於lock最經典的一個例子就是單例了,大家的實現都是執行個體化一個object對象,然後鎖住它,然後在判斷是否要執行個體要實現單例的那個對象。我們為什麼要執行個體化一個object,而不是直接lock(typeof(object)),那是因為這樣會把object這個類型給鎖住,鎖住期間,任何使用線程使用lock(typeof(object))就必須等待,object還是可以正常使用。lock能起到單線程訪問的原因是:它裡面有一個空的for死迴圈,一直在讀同步塊索引中的一個位,如果這個位沒有被標誌跳出迴圈,如果被標誌就一直執行迴圈,直到方法執行完成,其他線程就一直等待,現在你知道lock能使你的程式只能單線程反問也知道lock的效率低了吧。

 

NextObjPtr一個最牛B的指標。CLR中的所有資源都從託管堆中分配,託管堆是一塊連續的記憶體空間,維護一個指標NextObjPtr,它指向上一個對象地址的後面,下一個對象的開始位置,若託管堆中沒有對象就指向託管堆的開始位置,每分配一個對象就將NextObjPtr指向這個對象的後面,以準備開始分配下一個對象。NextObjptr指標移動的位置其實就是上一個對象所在空間的長度,從指向對象的開始位置改為對象的末尾嗎。從哪裡開始指派至就全靠NextObjPtr啦。

 

執行個體化一個對象需要多少空間?對象的所有欄位所需的記憶體+類型對象指標+同步塊索引。關於類型對象指標和同步塊索引的作用前面已經提過了。有些欄位沒有明顯定義,但它確確實實存在,每個對象除了object的對象都有base欄位,通過它可以調用父類的執行個體欄位和方法,通過它你可以訪問你爺爺的爺爺定義的欄位和方法。CLR用遞迴的方式調用父類的方法,當然也要看,你爺爺是否願意讓你調用,原因你懂的。

 

在記憶體回收開始之前速度比C快。對象就這樣開心的在託管堆中分配,託管堆的容量是有限的,總有一天第0代會滿,容不下一粒沙子。記憶體回收就出場了,在記憶體回收出場之前,你使用記憶體很happy,當然速度是非常快,比C語言的速度還快,因為C的記憶體是隨便分配,只要找到合適大小的地區,就在那裡分配記憶體了,這樣會導致記憶體片段,有時需要一塊大的記憶體,需要遍曆多處。記憶體回收的時候日子就不是那麼好過了。速度肯定比C慢了,看下面你就知道記憶體回收的時候,程式的速度為什麼慢了。

 

記憶體回收分兩步:1:標記;2:壓縮

1:標記。在記憶體回收開始的時候,記憶體回收行程視託管堆中的所有對象都為垃圾,即線程棧上沒有指標指向託管堆。這樣的估計是因為一個對象被視為垃圾就是它沒有被引用,當記憶體回收開始的時候,記憶體回收行程會沿著線程棧線性掃描,當線程棧上的一個變數引用了託管堆中的對象時,記憶體回收行程就會將這個對象標記,即修改該對象同步塊索引中的一個特定的位,同步塊索引就是一個bit數組,每一個元素都有它特定的作用,上面就列出了我所知道的三個功能。被標記的對象也可能引用其他的對象,而被引用的對象同樣會被標記,記憶體回收行程是用遞迴的方式將這些對象一一標記的,一個對象可能會被多個對象引用,當記憶體回收行程發現某個對象被標記時就會退出遞迴,因為再往下遞迴完全是多餘,而且還可能出現死迴圈。

記憶體回收行程就這樣線性掃描線程棧,遞迴的掃描託管堆,最後將託管堆中所有被引用的對象標記,而沒有被標記的對象就是垃圾,等著被回收。

 

2:壓縮。當垃圾被回收之後,就會出現磁碟片段,那麼就要對託管堆進行整理,即壓縮。將沒有被回收的對象放在一起,靠近託管堆開始的位置,將剩餘的記憶體騰出空間來以便存放新的對象。由於壓縮很多個物件就會移動位置,而引用他們的指標都會變得無效,所以託管堆要修改所有指標的指向,以保證不會因為記憶體回收而讓對象變得不可到達,指標變得無效。

壓縮完了之後,又騰出了空間,又可以分配新的對象,當第0代滿了之後又進行記憶體回收,記憶體回收就這樣一直進行著,直到回收了3代還是沒有記憶體可以分配,那就是彈盡糧絕的時候了,CLR會告訴你OutOfMemoryException。CLR的記憶體被的程式吃光了。更多關於代的資訊,可以看我的這篇部落格。在第0代滿的時候就會進行記憶體回收,第0代回收完之後還是沒有足夠的記憶體存放當前對象就回收第1代,如果還是不夠就回收第2代,夠就不回收下一代,記憶體回收還可以用代碼控制GC.Collect()。

更多關於記憶體管理和記憶體回收的內容,請等待我的下一篇部落格。

 

作者:陳太漢

部落格:http://www.cnblogs.com/hlxs/

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.