.NET的GC理解誤區

來源:互聯網
上載者:User
.NET的GC理解誤區


最近面試了一些人,發現對.NET的GC(記憶體回收)的理解都存在錯誤。GC其實是相當複雜的系統,雖然95%的情況下我們並不需要考慮它,但仍有5%的情況我們不得不接觸GC體系來解決問題。比如這個問題:

void Func()
{
  A a = new A();
  B b = new B();
  a.RefToB = b;
  b.RefToA = a;
}

那麼a和b會不會被GC回收?好幾個人都答錯。如果你按照COM的模式去思考GC,那就完全錯誤了。每次我問“什麼情況下,對象會被GC回收?”,他們都能回答上來“當程式裡沒有對對象的引用時”。但是錯了,為什嗎?如果你還沒明白,就再看看上面這段代碼。

GC管理對象不是用的COM的引用計數模式。事實上最初微軟確實想用引用計數方式實現GC,這樣的一個優勢就是對象的析構時機是確定的,當引用計數為0時,對象會被析構,之後也不會再有任何代碼能夠再訪問該對象,這是很理想的情況。但經過反覆實驗,這種方法被拋棄了。一個原因就是如上的例子,會導致對象無法釋放。另一個重要原因就是應用計數的額外開銷對高效能程式不可接受。尤其是在多線程情況下,因為.NET使用自由執行緒模式,多個線程可能同時訪問一個對象,每一個引用計數的增減操作都不得不做線程同步。

.NET採用的GC模式是分代GC(Generational GC),堆空間按對象的生存期長短分成3代。新分配的對象在第0代,按地址順序分配,當第0代的空間(約幾百K)用光時,將程式裡能引用到的對象移動到第1代,那麼剩下的就是垃圾,第0代空間便可以重新用於分配。同理,第1代也按同樣的邏輯運行,那麼第2代裡的對象將都是生存期很長的對象。由此可以推出如下幾點:
1)對象的分配時間開銷遠小於C++的堆分配,但回收時間開銷大於分配時間開銷。
2)不會出現C++裡的堆片段過多的問題,有利於程式長時間運行。
3)循環參考的對象能夠被正確回收。

那麼再回答開始的問題,“什麼情況下,對象會被GC回收?”正確答案是“當程式裡沒有對對象的活引用(Alive reference)時。”

當然,實際情況比如上所述複雜得多,有興趣可以考慮如下問題:
1)Finalizer在哪裡、什麼時候執行?如果拋了異常會有什麼後果?
2)大於第0代空間的大數組是怎麼分配的?
3)為什麼要求Dispose()可以被調用多次?

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.