C#下內存管理--垃圾收集


章節安排

  1. 內存管理簡介
  2. 垃圾回收機制
  3. 性能問題
  4. C#下非托管資源的處理
  5. 要強調的幾點
  6. References

內存管理簡介

      對於任何一種編程語言,內存管理都是不得不提很重要的一塊內容,但可惜的是目前為止沒有任何一種編程語言對內存管理處理的非常完美,每種語言都在兼顧性能效率,語法語義易用性等方面折中中有所側重。例如較之於C#,JAVA等語言C++號稱不需要垃圾收集,因為C++本身產生的垃圾很少,誠然這是C++的優勢,這也就是為什么在內存受限或者效率優先的環境下優先考慮C++,但它的缺點也是明顯的--程序員必須自己控制內存管理,很容易產生內存泄漏,這同時也造就了C++很難掌握。感謝摩爾定律吧,它促使了垃圾收集這個概念的出現,但較之C++直接操縱內存釋放,再牛逼的垃圾收集算法也無法抹去那一層性能上的損失。

      在討論之前我們先明確一點:內存中數據按所處位置不同可以分為棧內存和堆內存,棧的主要作用是追蹤函數調用之間的數據傳遞(棧上所存儲的數據類型通常是int,char,long,指針等內置值類型和struct。注意一點在多線程環境下,每個線程都有自己的棧。)所以棧的內存管理通常由操作系統負責。而我們所說的內存管理,大都討論的是堆上內存管理(分配在堆上的類型一般是自定義引用類型:類,接口,字符串,對象實例,C#中委托等)。關於這一點詳情請參照Under  the hood of NET Management。

       內存內存管理從生命周期上來分可以分為三個階段:內存分配,內存生命周期內管理,內存的釋放。每一階段都與程序的運行效率關系密切,以C++為例,在新版的C++標准中Unique_ptr取代auto_ptr,move語義,引入右值引用等措施極大地提高了STL的效率(詳細信息參考Refereces中關於C++的鏈接)。而兼顧討論內存管理的所有內容有點不現實,本篇主要關注內存的釋放,確切來講是C#的垃圾回收。    

垃圾回收機制       

       首先聲明一點所謂垃圾回收,回收的是分配在托管堆上的內存,對於托管堆外的內存,它無能為力。

       討論垃圾回收機制就不得不提內存的分配,在C運行時堆(C-runtime heap)中,堆是不連續的,我們new一個新的對象時,系統會檢查內存,找一塊足夠大的內存然后初始化對象,對象被銷毀后,這塊空間會用於初始化新的對象。這樣做有什么弊端?隨着程序運行一直有對象生成釋放,內存會變得碎片化,這樣有新的大的對象要生成時就必須擴展堆的長度,碎片內存無法得到充分利用,還有一個弊端是每次創建一個對象時都要檢查堆內存,效率不高。而C#托管堆采取連續內存存儲,新創建對象時只要考慮剩下的堆內存是否足夠大就成,但一直生成對象而不析構會使托管堆無限增大,怎么維護這樣一塊連續內存呢?這也就引出了垃圾回收機制。托管堆的大小是特定的,垃圾收集器GC負責當內存不夠的時候釋放掉垃圾對象,copy仍在使用的對象成一塊連續內存。而這就帶來了性能問題,當對象很大的時候,頻繁的copy移動對象會降低性能,所以C#的垃圾收集引入了世代和大對象堆小對象堆的概念。   

      所謂大對象堆小對象堆從字面意義就能看出其作用,大對象堆主要負責分配大的對象,小對象堆分配小的。但對象大小怎么確定呢?在.NET Framework中規定,如果對象大於或等於 85,000 字節,將被視為大型對象。當對象分配請求傳入后,如果符合該大小閾值,便會將此對象分配給大型對象堆。這個85000字節是根據性能優化的結果確定。值得注意的是垃圾回收對大對象堆和小對象堆都起作用。那么分大對象和小對象堆作用是什么呢?還是性能,對於大對象和小對象區別對待采取不同靈活的垃圾回收策略必定比一棍子打死死板的采用同一種策略要好。下面我們討論一下在SOH和LOH不同的垃圾收集策略:   

      先說一下世代,之所以分世代,是因為在第0代就能清除大部分對象。請注意,世代是個邏輯上的概念,物理上並沒有世代這個數據結構。以小對象堆垃圾回收為例:當一個對象被創建的時候,它被定義為第0代對象,而經歷一次垃圾收集后還存余的對象就被歸入了第1代對象,同理經過兩次或者兩次以上仍然存在的對象就可以看成第2代對象。雖然世代只是邏輯概念,但它卻是有大小的,對於SOH對象來說,由於每次垃圾回收都會壓縮移動對象,所以世代數越大越在堆底。經歷一次垃圾回收,對象都會被移入下一個世代的內存空間中(下圖以小對象堆上垃圾回收為例。對象W至少經過兩次垃圾回收而不死,所以放入世代2,X經歷了一次垃圾回收)。而每次一個世代內存達到其闕值,都會引發垃圾收集器回收一次。這么做的好處就是每次垃圾回收器只是回收一個世代的內存,從整體上來看,減少了對象復制移動的次數。  

       

         以上討論都是針對於SOH,對於SOH對象來說復制移動較之LOH成本性能要小一點,那么對於LOH呢?復制一個LOH的對象並且清除原來內存位置的字節,成本相當大,怎么確保它的垃圾回收呢?首先從物理上來說,LOH在托管堆的堆底,SOH在其上,邏輯上講LOH對象都分配在第二世代,也就是說前邊第0代和第1代的垃圾收集回收的都是第OH對象,這也就解釋了為什么前兩個世代垃圾回收中允許復制移動對象。但對於第二世代垃圾回收呢?第二代垃圾回收之后的對象仍是第二世代,其回收時並不移動仍在使用的對象,壓縮空間,而只是清除垃圾對象。當一個新LOH對象創建時,它會從堆底遍歷尋找LOH中能夠滿足要求的內存,如果沒有接着向堆頂創建(這個過程和C運行時工作原理一樣,所以也存在相同的弊端,LOH堆內存有可能存在碎片)。此時如果堆頂已經超出闕值,引發垃圾回收器回收內存空間。

         從上邊討論中我們可以總結一下:我們new出一對象時它要么小對象會被放入第0代,大對象會被分在LOH中,只有垃圾回收器才能夠在第1代和第2代中“分配”對象,這里所說分配對象是指移動復制對象。

         以上就是垃圾回收機制,有一塊最重要的一點沒有討論,就是垃圾收集器GC怎么判斷該對象是垃圾對象。關於這一點可以參考鏈接。

性能問題

         由上邊討論我們可以看出,自動化垃圾回收是需要付出成本的,而世代和大對象堆/小對象堆這些概念的引入是盡可能的降低這一成本。但性能問題不可避免,性能數據分析可以很好的幫助我們了解避免些許問題,關於性能分析工具及方法請參考References中鏈接。

C#下非托管資源的處理--Disposable模式

         我們上邊說過,垃圾回收器只能收集托管堆的內存,但對於堆外內存比如HWnds,數據庫連接,GDI句柄,safeHandle。這樣就有一個問題:垃圾收集器不會確定地運行,其結果可能會使您的對象在上次引用之后很長時間不能被終結。如果你的對象占用了昂貴或稀少的資源(如一個數據庫連接),這是不能被接受的。為了避免無休止地等待垃圾收集器運行,擁有資源的類型應該實現 IDisposable 接口,然后該類型資源的使用方會及時地釋放那些資源。Joe Duff在其網站中給了相關注意細節,並提供了一種Disposable模式。可以參考一下鏈接了解一下,使你的程序寫的更加優雅健壯。(MSDN關於IDisposable例子中也采用這種方法)

         http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=88e62cdf-5919-4ac7-bc33-20c06ae539ae

要強調的幾點

         1. 值得注意的是雖然微軟目前不會移動壓縮LOH,但是將來可能會,所以如果分配了大型對象並希望確保它們位置不被移動,則應該將其固定起來。

         2. 這篇文章是基於本人理解,有可能有出入,詳細信息可以參照鏈接,並歡迎指正。

 

References

C++

http://en.cppreference.com/w/

http://zh.wikipedia.org/wiki/C++0x

http://blog.csdn.net/zentropy/article/details/6973411

http://www.codeproject.com/Articles/71540/Explicating-the-new-C-standard-C-0x-and-its-implem#RValues http://www.codeproject.com/Articles/101886/Standard-C-Library-Changes-in-Visual-C-2010

C#

http://msdn.microsoft.com/zh-cn/magazine/bb985011(en-us).aspx

http://msdn.microsoft.com/zh-cn/magazine/cc534993.aspx

http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=88e62cdf-5919-4ac7-bc33-20c06ae539ae

性能問題和多語言交互

http://msdn.microsoft.com/zh-cn/magazine/ee309515.aspx

http://msdn.microsoft.com/zh-cn/magazine/cc163528.aspx

http://msdn.microsoft.com/zh-cn/magazine/cc163316.aspx

http://msdn.microsoft.com/zh-cn/magazine/cc163392.aspx

垃圾收集發展史:

http://blog.csdn.net/KAI3000/article/details/314628

http://blog.csdn.net/hellothere/article/details/2115422

http://blog.csdn.net/hellothere/article/details/2245734


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM