深入了解C系列談談C中垃圾回收與內存管理機制_第1頁
深入了解C系列談談C中垃圾回收與內存管理機制_第2頁
深入了解C系列談談C中垃圾回收與內存管理機制_第3頁
深入了解C系列談談C中垃圾回收與內存管理機制_第4頁
深入了解C系列談談C中垃圾回收與內存管理機制_第5頁
已閱讀5頁,還剩7頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、深入了解C#系列:談談C#中垃圾回收與內存管理機制今天抽空來討論一下.Net的垃圾回收與內存管理機制,也算是完成上個WCF分布式開發(fā)必備知識系列后的一次休息吧。以前被別人面試的時候問過我GC工作原理的問題,我現在面試新人的時候偶爾也會問相關的問題。那么你是否也遇到這樣的問題呢?比如你清楚.Net的垃圾回收機制嗎?你能簡述一下GC的工作原理嗎?怎么樣才能有效的管理內存呢?Using語句體內實例化的對象有什么作用?等等相關問題。下面我們就來詳細討論一下。相信你看完以后也可以面試別人。     1.Net的類型和內存分配    &

2、#160;Net中的所有類型都是(直接或間接)從System.Object類型派生的。    CTS中的類型被分成兩大類引用類型(reference type,又叫托管類型managed type),分配在內存堆上,值類型(value type)。值類型分配在堆棧上。如圖         值類型在棧里,先進后出,值類型變量的生命有先后順序,這個確保了值類型變量在推出作用域以前會釋放資源。比引用類型更簡單和高效。堆棧是從高地址往低地址分配內存。     引用

3、類型分配在托管堆(Managed Heap)上,聲明一個變量在棧上保存,當使用new創(chuàng)建對象時,會把對象的地址存儲在這個變量里。托管堆相反,從低地址往高地址分配內存,如圖      2.GC垃圾收集器的工作原理      上圖中,當dataSet使用過期以后,我們不顯示銷毀對象,堆上的對象還繼續(xù)存在,等待GC的 回收。垃圾收集器通過分代支持對象的年齡化是推薦的但不是必需的。一代在內存里是一個具有相對年齡的對象的單位。對象的代號或年齡標識對象屬于那個分代。在應用程序的生命周期里,越近創(chuàng)建的對象屬于

4、越新的代,并且比早創(chuàng)建的對象具有較低的分代號。最近分代里的對象代號是0.      在new對象時,要先搜索空閑鏈表,找到最適合內存塊,分配,調整內存塊鏈表,合并碎片。new操作幾乎可以在O(1)的時間完成,把 堆頂指針加1。工作原理是: 當托管堆上剩余空間不足,或者Generator 0 的空間已滿的時候GC運行,開始回收內存。垃圾回收的開始,GC對堆內存的壓縮調整,對象集中到頂部。GC在掃描垃圾的時候會占用一定的CPU時間片的, 最初的GC算法真的是掃描整個堆,效率低?,F在的GC把堆中的對象分成3代,最近進入堆的是第 0

5、代(generation 0), 其次是generation 1, generation2. 第一次GC只掃描第0代。如果回收的空間足夠當前使用就不必掃描其它generation的對象。所以,GC創(chuàng)建對象的效率比C+高效,不需要掃描全部 堆空間。它通過掃描策略,再加上內存管理策略帶來的性能提升,足以補償GC所占用的CPU時間。    3.什么是非托管資源常見 的非托管資源就是包裝操作系統(tǒng)資源的對象,例如文件,窗口或網絡連接,對于這類資源雖然垃圾回收器可以跟蹤封裝非托管資源的對象的生存期,但它知道如何清 理這些資源。好在.net Framework提供的Finali

6、ze()方法,它允許在垃圾回收器回收該類資源前,適當的清理非托管資源。這里列舉幾種常見的非托管資源:畫筆、流 對象、組件對象等等資源 (Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter,ApplicationContext,Brush,Component,ComponentDesigner,Container,Context,Cursor,FileStream,Font,Icon,Image,Matrix,Timer,Tooltip)。(參考MSDN)    4.如何有效釋放非托

7、管資源。     GC無法管理非托管資源,那么如何釋放非托管資源呢?.Net提供了兩種方式:(1)析構函數:垃圾收集器回收非托管對象的資源時,會調用對象的終結方法Finalize(),進行資源的清理工作,但是由于GC工作規(guī)則的限制,GC調用對象的Finalize方法,第一次不會釋放資源,第二次調用之后才刪除對象。(2)繼承IDisposable接口,實現Dispose()方法,IDisposable接口定義了一個模式(具有語言級的支持),為釋放未托管的資源提供了確定的機制,并避免產生析構函數固有的與垃圾收集器相關的問題。   

8、;為了更好的理解垃圾回收機制,我特地寫了部分代碼,里面添加了詳細的注釋。定義單個類FrankClassWithDispose(繼承接口IDisposable)、FrankClassNoFinalize(沒終結器)、FrankClassWithDestructor(定義了析構函數)。具體代碼如下:-1using System; 2using 3using System.Text; 4using System.Data; 5using 6using System.Drawing; 7/Coded&#

9、160;By Frank Xu Lei 18/2/2009 8/Study the .NET Memory Management 9/Garbage Collector 垃圾收集器。可以根據策略在需要的時候回收托管資源,10/但是GC不知道如何管理非托管資源。如網絡連接、數據庫連接、畫筆、組件等11/兩個機制來解決非托管資源的釋放問題。析構函數、IDispose接口12/COM引用計數13/C+手動管理,New Delete14/VB自動管理15namespace&

10、#160;MemoryManagement1617    /繼承接口IDisposable,實現Dispose方法,可以釋放FrankClassDispose的實例資源18    public class FrankClassWithDispose : IDisposable19    20        private OdbcConnectio

11、n _odbcConnection = null;21        22        /構造函數23        public FrankClassWithDispose()24        25   

12、         if (_odbcConnection = null)26                _odbcConnection = new OdbcConnection();27        &#

13、160;   Console.WriteLine("FrankClassWithDispose has been created ");28        29        /測試方法30        public void DoSomethin

14、g()31        3233            /*/code here to do something34            return 35      

15、;  36        /實現Dispose,釋放本類使用的資源37        public void Dispose()38        39            if (_od

16、bcConnection != null)40                _odbcConnection.Dispose();41            Console.WriteLine("FrankClassWithDispose has been d

17、isposed");42        43    44    /沒有實現Finalize,等著GC回收FrankClassFinalize的實例資源,GC運行時候直接回收45    public class FrankClassNoFinalize46    47     &#

18、160;  private OdbcConnection _odbcConnection = null;48        /構造函數49        public FrankClassNoFinalize()50        51    

19、60;       if (_odbcConnection = null)52                _odbcConnection = new OdbcConnection();53          

20、;  Console.WriteLine("FrankClassNoFinalize  has been created");54        55        /測試方法56        public void DoSomething()57 

21、;       5859            /GC.Collect();60            /*/code here to do something61       &

22、#160;    return 62        63    64    /實現析構函數,編譯為Finalize方法,調用對象的析構函數65    /GC運行時,兩次調用,第一次沒釋放資源,第二次才釋放66    /FrankClassDestructor的實例資源67   &

23、#160;/CLR使用獨立的線程來執(zhí)行對象的Finalize方法,頻繁調用會使性能下降68    public class FrankClassWithDestructor69    70        private OdbcConnection _odbcConnection = null;71      

24、0; /構造函數72        public FrankClassWithDestructor()73        74            if (_odbcConnection = null)75     &

25、#160;          _odbcConnection = new OdbcConnection();76            Console.WriteLine("FrankClassWithDestructor  has been created");77  

26、;      78        /測試方法79        public void DoSomething()80        81            

27、;/*/code here to do something8283            return 84        85        /析構函數,釋放未托管資源86        

28、FrankClassWithDestructor()87        88            if (_odbcConnection != null)89                _odbcConn

29、ection.Dispose();90            Console.WriteLine("FrankClassWithDestructor  has been disposed");91        92    9394其中使用了非托管的對象OdbcConnection的實例。建立的客戶

30、端進行了簡單的測試??蛻舳舜a如下:-1using System; 2using 3using System.Text; 4using System.Data; 5using MemoryManagement; 6/Coded By Frank Xu Lei 18/2/2009 7/Study the .NET Memory Management 8/Test The Unma

31、naged Objects Reclaimed. 9/針對非托管代碼的測試,比較10/托管代碼,GC可以更具策略自己回收,也可以實現IDisposable,調用Dispose()方法,主動釋放。11namespace MemoryManagementClient1213    class Program14    15        static void Main(

32、string args)16        1718            /*/(1)/19            /調用Dispose()方法,主動釋放。資源,靈活20        &

33、#160;   FrankClassWithDispose _frankClassWithDispose = null;21            try22            23         

34、60;      _frankClassWithDispose = new FrankClassWithDispose();24                _frankClassWithDispose.DoSomething();25         &#

35、160;      26            27            finally28            29     

36、;           if (_frankClassWithDispose!=null)30                _frankClassWithDispose.Dispose();31          &#

37、160;     /Console.WriteLine("FrankClassWithDispose實例已經被釋放");32            33                34     &

38、#160;      /*/(2)/35            /可以使用Using語句創(chuàng)建非托管對象,方法執(zhí)行結束前,會調用36            using (FrankClassWithDispose _frankClassWithDispose2 =

39、0;new FrankClassWithDispose()37            38                /_frankClassWithDispose2.DoSomething();39         

40、;   4041            /*/(3)/42            /垃圾收集器運行的時候,一次就釋放資源43            FrankClassNoFinalize _fran

41、kClassNoFinalize = new FrankClassNoFinalize();44            _frankClassNoFinalize.DoSomething();45             46        

42、;    /*/(4)/47            /垃圾收集器運行的時候,兩次才能夠釋放資源48            FrankClassWithDestructor _frankClassWithDestructor = new FrankClassWithDestruct

43、or();49            _frankClassWithDestructor.DoSomething();50            /*/(5)/51            /不能使用Using語句來創(chuàng)建對象,因為其沒實現ID

44、ispose接口52            /using (FrankClassWithDestructor _frankClassWithDestructor2 = new FrankClassWithDestructor()53            /54    

45、        /    _frankClassWithDestructor2.DoSomething();55            /5657            /*/58     

46、       /For Debug59            Console.WriteLine("Press any key to continue");60            Console.ReadLine()

47、;6162        63        64    6566 有些時候資源必須在特定時間釋放,類可以實現執(zhí)行資源管理和清除任務方法IDisposable.Dispose的接口IDisposable。如果調用者需要調用Dispose方法清理對象,類作為契約的一部分必須實現Dispose方法。垃圾收集器默認情況下不會調用Dispose方法;然而,實現Dispose方法可以調用GC里的方

48、法去規(guī)范垃圾收器的終結行為。值得一提的是:調用Dispose()方法,主動釋放資源,靈活,可以使用Using語句創(chuàng)建非托管對象,方法執(zhí)行結束前,會調用Dispose()方法釋放資源,這兩端代碼的效果是一樣的,可以查看編譯后IL。1.try 2   3    IL_0003:  nop 4    IL_0004:  newobj     instance void

49、60;MemoryManagementMemoryManagement.FrankClassWithDispose:.ctor() 5    IL_0009:  stloc.0 6    IL_000a:  ldloc.0 7    IL_000b:  callvirt   instance void MemoryManagementMe

50、moryManagement.FrankClassWithDispose:DoSomething() 8    IL_0010:  nop 9    IL_0011:  nop10    IL_0012:  leave.s    IL_002811    / end .try12 

51、0;finally13  14    IL_0014:  nop15    IL_0015:  ldloc.016    IL_0016:  ldnull17    IL_0017:  ceq18    IL_0019:  stloc.s   

52、60;CS$4$000019    IL_001b:  ldloc.s    CS$4$000020    IL_001d:  brtrue.s   IL_002621    IL_001f:  ldloc.022    IL_0020:  callvirt  

53、0;instance void MemoryManagementMemoryManagement.FrankClassWithDispose:Dispose()23    IL_0025:  nop24    IL_0026:  nop25    IL_0027:  endfinally26    / end handler27 

54、; IL_0028:  nop28  IL_0029:  newobj     instance void MemoryManagementMemoryManagement.FrankClassWithDispose:.ctor()29  IL_002e:  stloc.130  .try31  32    IL_002f: &

55、#160;nop33    IL_0030:  nop34    IL_0031:  leave.s    IL_004535    / end .try36  finally37  38    IL_0033:  ldloc.139    

56、;IL_0034:  ldnull40    IL_0035:  ceq41    IL_0037:  stloc.s    CS$4$000042    IL_0039:  ldloc.s    CS$4$000043    IL_003b:  brtrue

57、.s   IL_004444    IL_003d:  ldloc.145    IL_003e:  callvirt   instance void mscorlibSystem.IDisposable:Dispose()46    IL_0043:  nop47    IL_0044: 

58、; endfinally48    / end handler49Using 語句有同樣的效果,來實現非托管對象資源的釋放。這點在面試中也會經常遇到,Using關鍵字的用法有哪幾種等等類似的問題。基本理想的答案都是除了引用 命名空間,和命名空間設置別名外,就是這個用法實現如try finally塊一樣作用的對非托管對象資源的回收。只是一種簡便的寫法。當你用Dispose方法釋放未托管對象的時候,應該調用GC.SuppressFinalize。如果對象 正在終結隊列(finalization queue),GC.Suppres

59、sFinalize會阻止GC調用Finalize方法。因為Finalize方法的調用會犧牲部分性能。如果你的 Dispose方法已經對委托管資源作了清理,就沒必要讓GC再調用對象的Finalize方法(MSDN)。附上MSDN的代碼,大家可以參考.-public class BaseResource: IDisposable   / 指向外部非托管資源   private IntPtr handle;   / 此類使用的其它托管資源.   private Component Components; 

60、60; / 跟蹤是否調用.Dispose方法,標識位,控制垃圾收集器的行為   private bool disposed = false;   / 構造函數   public BaseResource()         / Insert appropriate constructor code here.      / 實現接口IDisposable.   / 不能聲明為虛方法virtual. 

61、;  / 子類不能重寫這個方法.   public void Dispose()         Dispose(true);      / 離開終結隊列Finalization queue       / 設置對象的阻止終結器代碼      /       GC.SuppressFinalize(

62、this);      / Dispose(bool disposing) 執(zhí)行分兩種不同的情況.   / 如果disposing 等于 true, 方法已經被調用   / 或者間接被用戶代碼調用. 托管和非托管的代碼都能被釋放   / 如果disposing 等于false, 方法已經被終結器 finalizer 從內部調用過,   /你就不能在引用其他對象,只有非托管資源可以被釋放。   protected virtual void Dispose(b

63、ool disposing)         / 檢查Dispose 是否被調用過.      if(!this.disposed)               / 如果等于true, 釋放所有托管和非托管資源          if(disposing)                     / 釋放托管資源.            Components.Dispose();  

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論