国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

C#垃圾回收與內存管理【轉】

2019-11-17 04:01:08
字體:
來源:轉載
供稿:網友
  原:http://www.mikel.cn/article.asp?id=1698

你清楚.Net的垃圾回收機制嗎?你能簡述一下GC的工作原理嗎?怎么樣才能有效的管理內存呢?Using語句體內實例化的對象有什么作用?


     本節的組織如下,1..Net的類型和內存分配2.GC垃圾收集器的工作原理3.什么是非托管資源4.如何有效釋放對象資源??偨Y.現在開始我們本節的學習。

    1..Net的類型和內存分配

     Net中的所有類型都是(直接或間接)從 System.Object 類型派生的。

    CTS 中的類型被分成兩大類——引用類型( reference type ,又叫托管類型 [managed type] ),分配在內存堆上,值類型( value type )。值類型分配在堆棧上。如圖

     

     值類型在棧里,先進后出,值類型變量的生命有先后順序,這個確保了值類型變量在推出作用域以前會釋放資源。比引用類型更簡單和高效。堆棧是從高地址往低地址分配內存。

     引用類型分配在托管堆(Managed Heap)上,聲明一個變量在棧上保存,當使用new創建對象時,會把對象的地址存儲在這個變量里。托管堆相反,從低地址往高地址分配內存,如圖

   

    2.GC垃圾收集器的工作原理

      上圖中,當dataSet使用過期以后,我們不顯示銷毀對象,堆上的對象還繼續存在,等待GC的 回收。

垃圾收集器通過分代支持對象的年齡化是推薦的但不是必需的。一代在內存里是一個具有相對年齡的對象的單位。對象的

代號或年齡標識對象屬于那個分代。在應用程序的生命周期里,越近創建的對象屬于越新的代,并且比早創建的對象具有

較低的分代號。最近分代里的對象代號是0.

      在new對象時,要先搜索空閑鏈表,找到最適合內存塊,分配,調整內存塊鏈表,合并碎片。new操作幾乎可以在O(1)的時間完成,把堆頂指針加1。工作原理是: 當托管堆上剩余空間不足,或者Generator 0 的空間已滿的時候GC運行,開始回收內存。垃圾回收的開始,GC對堆內存的壓縮調整,對象集中到頂部。GC在掃描垃圾的時候會占用一定的CPU時間片的,最初的GC算法真的是掃描整個堆,效率低?,F在的GC把堆中的對象分成3代,最近 進入堆的是第0代(generation 0), 其次是generation 1, generation2. 第一次GC只掃描第0代。如果回收的空間足夠當前使用就不必掃描其它generation的對象。所以,GC創建對象的效率比C++高效,不需要掃描全部堆空間。它通過掃描策略,再加上內存管理策略帶來的性能提升,足以補償GC所占用的CPU時間。

    3.什么是非托管資源

  常見的非托管資源就是包裝操作系統資源的對象,例如文件,窗口或網絡連接,對于這類資源雖然垃圾回收器可以跟蹤封裝非托管資源的對象的生存期,但它知道如何清理這些資源。好在.net Framework提供的Finalize()方法,它允許在垃圾回收器回收該類資源前,適當的清理非托管資源。這里列舉幾種常見的非托管資源:畫筆、流對象、組件對象等等資源(Object,OdbcDataReader,OleDBDataReader,Pen,Regex,Socket,StreamWriter,applicationContext,Brush,

Component,ComponentDesigner,Container,Context,Cursor,FileStream,

Font,Icon,Image,Matrix,Timer,Tooltip)。(參考MSDN)

    4.如何有效釋放非托管資源。

     GC無法管理非托管資源,那么如何釋放非托管資源呢?.Net提供了兩種方式:

(1)析構函數:垃圾收集器回收非托管對象的資源時,會調用對象的終結方法Finalize(),進行資源的清理工作,但是由于GC工作規則的限制,GC調用對象的Finalize方法,第一次不會釋放資源,第二次調用之后才刪除對象。

(2)繼承IDisposable 接口,實現Dispose()方法,IDisposable接口定義了一個模式(具有語言級的支持),為釋放未托管的資源提供了確定的機制,并避免產生析構函數固有的與垃圾收集器相關的問題。

   為了更好的理解垃圾回收機制,我特地寫了部分代碼,里面添加了詳細的注釋。定義單個類FrankClassWithDispose(繼承接口IDisposable ) 、FrankClassNoFinalize(沒終結器) 、FrankClassWithDestructor(定義了析構函數)。

具體代碼如下:

  Code
1  using  System;
2  using  System.Collections.Generic;
3  using  System.Text;
4  using  System.Data;
5  using  System.Data.Odbc;
6  using  System.Drawing;
7  // Coded By Frank Xu Lei 18/2/2009
8  // Study the .NET Memory Management
9  // Garbage Collector 垃圾收集器??梢愿鶕呗栽谛枰臅r候回收托管資源,
10  // 但是GC不知道如何管理非托管資源。如網絡連接、數據庫連接、畫筆、組件等
11  // 兩個機制來解決非托管資源的釋放問題。析構函數、IDispose接口
12  // COM引用計數
13  // C++手動管理,New Delete
14  // VB自動管理
15  namespace  MemoryManagement
16    {
17       // 繼承接口IDisposable,實現Dispose方法,可以釋放FrankClassDispose的實例資源
18       public   class  FrankClassWithDispose : IDisposable
19         {
20           PRivate  OdbcConnection _odbcConnection  =   null ;
21          
22           // 構造函數
23           public  FrankClassWithDispose()
24             {
25               if  (_odbcConnection  ==   null )
26                  _odbcConnection  =   new  OdbcConnection();
27              Console.WriteLine( " FrankClassWithDispose has been created  " );
28          }
29           // 測試方法
30           public   void  DoSomething()
31             {
32  
33                /**/ /// /code here to do something
34               return  ;
35          }
36           // 實現Dispose,釋放本類使用的資源
37           public   void  Dispose()
38             {
39               if  (_odbcConnection  !=   null )
40                  _odbcConnection.Dispose();
41              Console.WriteLine( " FrankClassWithDispose has been disposed " );
42          }
43      }
44       // 沒有實現Finalize,等著GC回收FrankClassFinalize的實例資源,GC運行時候直接回收
45       public   class  FrankClassNoFinalize
46         {
47           private  OdbcConnection _odbcConnection  =   null ;
48           // 構造函數
49           public  FrankClassNoFinalize()
50             {
51               if  (_odbcConnection  ==   null )
52                  _odbcConnection  =   new  OdbcConnection();
53              Console.WriteLine( " FrankClassNoFinalize  has been created " );
54          }
55           // 測試方法
56           public   void  DoSomething()
57             {
58  
59               // GC.Collect();
60                /**/ /// /code here to do something
61               return  ;
62          }
63      }
64       // 實現析構函數,編譯為Finalize方法,調用對象的析構函數
65       // GC運行時,兩次調用,第一次沒釋放資源,第二次才釋放
66       // FrankClassDestructor的實例資源
67       // CLR使用獨立的線程來執行對象的Finalize方法,頻繁調用會使性能下降
68       public   class  FrankClassWithDestructor
69         {
70           private  OdbcConnection _odbcConnection  =   null ;
71           // 構造函數
72           public  FrankClassWithDestructor()
73             {
74               if  (_odbcConnection  ==   null )
75                  _odbcConnection  =   new  OdbcConnection();
76              Console.WriteLine( " FrankClassWithDestructor  has been created " );
77          }
78           // 測試方法
79           public   void  DoSomething()
80             {
81                /**/ /// /code here to do something
82  
83               return  ;
84          }
85           // 析構函數,釋放未托管資源
86           ~ FrankClassWithDestructor()
87             {
88               if  (_odbcConnection  !=   null )
89                  _odbcConnection.Dispose();
90              Console.WriteLine( " FrankClassWithDestructor  has been disposed " );
91          }
92      }
93  }
94  
其中使用了非托管的對象OdbcConnection 的實例。建立的客戶端進行了簡單的測試。客戶端代碼如下:

  Code
1  using  System;
2  using  System.Collections.Generic;
3  using  System.Text;
4  using  System.Data;
5  using  MemoryManagement;
6  // Coded By Frank Xu Lei 18/2/2009
7  // Study the .NET Memory Management
8  // Test The Unmanaged Objects Reclaimed.
9  // 針對非托管代碼的測試,比較
10  // 托管代碼,GC可以更具策略自己回收,也可以實現IDisposable,調用Dispose()方法,主動釋放。
11  namespace  MemoryManagementClient
12    {
13       class  Program
14         {
15           static   void  Main( string [] args)
16             {
17  
18                /**/ /////////////////////////////////////// //(1) ////////////////////////////////////////// //
19               // 調用Dispose()方法,主動釋放。資源,靈活
20              FrankClassWithDispose _frankClassWithDispose  =   null ;
21               try
22                 {
23                  _frankClassWithDispose  =   new  FrankClassWithDispose();
24                  _frankClassWithDispose.DoSomething();
25                  
26              }
27               finally
28                 {
29                   if  (_frankClassWithDispose != null )
30                  _frankClassWithDispose.Dispose();
31                   // Console.WriteLine("FrankClassWithDispose實例已經被釋放 ");
32              }
33                  
34                /**/ /////////////////////////////////////// //(2) ///////////////////////////////////////////// /
35               // 可以使用Using語句創建非托管對象,方法執行結束前,會調用
36               using  (FrankClassWithDispose _frankClassWithDispose2  =   new  FrankClassWithDispose())
37                 {
38                   // _frankClassWithDispose2.DoSomething();
39              }
40  
41                /**/ /////////////////////////////////////// //(3) ////////////////////////////////////////// //
42               // 垃圾收集器運行的時候,一次就釋放資源
43              FrankClassNoFinalize _frankClassNoFinalize  =   new  FrankClassNoFinalize();
44              _frankClassNoFinalize.DoSomething();
45               
46                /**/ ////////////////////////////////////////// (4) ///////////////////////////////////////////// /
47               // 垃圾收集器運行的時候,兩次才能夠釋放資源
48              FrankClassWithDestructor _frankClassWithDestructor  =   new  FrankClassWithDestructor();
49              _frankClassWithDestructor.DoSomething();
50                /**/ ////////////////////////////////////////// /(5) /////////////////////////////////////////////
51               // 不能使用Using語句來創建對象,因為其沒實現IDispose接口
52               // using (FrankClassWithDestructor _frankClassWithDestructor2 = new FrankClassWithDestructor())
53               // {
54               //     _frankClassWithDestructor2.DoSomething();
55               // }
56  
57                /**/ //////////////////////////////////////////////////////////////////////////////////// //
58               // For Debug
59              Console.WriteLine( " Press any key to continue " );
60              Console.ReadLine();
61  
62          
63          }
64      }
65  }
66  
有些時候資源必須在特定時間釋放,類可以實現執行資源管理和清除任務方法IDisposable.Dispose的接口IDisposable。
如果調用者需要調用Dispose方法清理對象,類作為契約的一部分必須實現Dispose方法。垃圾收集器默認情況下不會調用
Dispose方法;然而,實現Dispose方法可以調用GC里的方法去規范垃圾收器的終結行為。

值得一提的是:調用Dispose()方法,主動釋放資源,靈活,可以使用Using語句創建非托管對象,方法執行結束前,會調用
Dispose()方法釋放資源, 這兩端代碼的效果是一樣的,可以查看編譯后IL。

  Code
1  . try
2       {
3      IL_0003:  nop
4      IL_0004:  newobj     instance  void  [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
5      IL_0009:  stloc. 0
6      IL_000a:  ldloc. 0
7      IL_000b:  callvirt   instance  void  [MemoryManagement]MemoryManagement.FrankClassWithDispose::DoSomething()
8      IL_0010:  nop
9      IL_0011:  nop
10      IL_0012:  leave.s    IL_0028
11    }    //  end .try
12     finally
13       {
14      IL_0014:  nop
15      IL_0015:  ldloc. 0
16      IL_0016:  ldnull
17      IL_0017:  ceq
18      IL_0019:  stloc.s    CS$ 4 $ 0000
19      IL_001b:  ldloc.s    CS$ 4 $ 0000
20      IL_001d:  brtrue.s   IL_0026
21      IL_001f:  ldloc. 0
22      IL_0020:  callvirt   instance  void  [MemoryManagement]MemoryManagement.FrankClassWithDispose::Dispose()
23      IL_0025:  nop
24      IL_0026:  nop
25      IL_0027:  endfinally
26    }    //  end handler
27    IL_0028:  nop
28    IL_0029:  newobj     instance  void  [MemoryManagement]MemoryManagement.FrankClassWithDispose::.ctor()
29    IL_002e:  stloc. 1
30    . try
31       {
32      IL_002f:  nop
33      IL_0030:  nop
34      IL_0031:  leave.s    IL_0045
35    }    //  end .try
36     finally
37       {
38      IL_0033:  ldloc. 1
39      IL_0034:  ldnull
40      IL_0035:  ceq
41      IL_0037:  stloc.s    CS$ 4 $ 0000
42      IL_0039:  ldloc.s    CS$ 4 $ 0000
43      IL_003b:  brtrue.s   IL_0044
44      IL_003d:  ldloc. 1
45      IL_003e:  callvirt   instance  void  [mscorlib]System.IDisposable::Dispose()
46      IL_0043:  nop
47      IL_0044:  endfinally
48    }    //  end handler
49  
Using 語句有同樣的效果,來實現非托管對象資源的釋放。這點在面試中也會經常遇到,Using關鍵字的用法有哪幾種等等類似的問題。基本理想的答案都是除了引用命名空間,和命名空間設置別名外,就是這個用法實現如try finally塊一樣作用的對非托管對象資源的回收。只是一種簡便的寫法。

     當你用Dispose方法釋放未托管對象的時候,應該調用GC.SuppressFinalize。如果對象正在終結隊列(finalization queue),GC.SuppressFinalize會阻止GC調用Finalize方法。因為Finalize方法的調用會犧牲部分性能。如果你的Dispose方法已經對委托管資源作了清理,就沒必要讓GC再調用對象的Finalize方法(MSDN)。附上MSDN的代碼, 大家可以 參考.

  Code
public   class  BaseResource: IDisposable
   {
     //  指向外部非托管資源
     private  IntPtr handle;
     //  此類使用的其它托管資源.
     private  Component Components;
     //  跟蹤是否調用.Dispose方法,標識位,控制垃圾收集器的行為
     private   bool  disposed  =   false ;

     //  構造函數
     public  BaseResource()
       {
        //  Insert appropriate constructor code here.
    }

     //  實現接口IDisposable.
     //  不能聲明為虛方法virtual.
     //  子類不能重寫這個方法.
     public   void  Dispose()
       {
       Dispose( true );
        //  離開終結隊列Finalization queue
        //  設置對象的阻止終結器代碼
        //   
       GC.SuppressFinalize( this );
    }

     //  Dispose(bool disposing) 執行分兩種不同的情況.
     //  如果disposing 等于 true, 方法已經被調用
     //  或者間接被用戶代碼調用. 托管和非托管的代碼都能被釋放
     //  如果disposing 等于false, 方法已經被終結器 finalizer 從內部調用過,
     // 你就不能在引用其他對象,只有非托管資源可以被釋放。
     protected   virtual   void  Dispose( bool  disposing)
       {
        //  檢查Dispose 是否被調用過.
        if ( ! this .disposed)
          {
           //  如果等于true, 釋放所有托管和非托管資源  
           if (disposing)
             {
              //  釋放托管資源.
             Components.Dispose();
          }
           //  釋放非托管資源,如果disposing為 false,
           //  只會執行下面的代碼.
          CloseHandle(handle);
          handle  =  IntPtr.Zero;
           //  注意這里是非線程安全的.
           //  在托管資源釋放以后可以啟動其它線程銷毀對象,
           //  但是在disposed標記設置為true前
           //  如果線程安全是必須的,客戶端必須實現。

       }
       disposed  =   true ;         
    }
          //  使用interop 調用方法
          //  清除非托管資源.
         [System.Runtime.InteropServices.DllImport( " Kernel32 " )]
          private   extern   static  Boolean CloseHandle(IntPtr handle);

     //  使用C# 析構函數來實現終結器代碼
     //  這個只在Dispose方法沒被調用的前提下,才能調用執行。
     //  如果你給基類終結的機會.
     //  不要給子類提供析構函數.
     ~ BaseResource()      
       {
        //  不要重復創建清理的代碼.
        //  基于可靠性和可維護性考慮,調用Dispose(false) 是最佳的方式
       Dispose( false );
    }

     //  允許你多次調用Dispose方法,
     //  但是會拋出異常如果對象已經釋放。
     //  不論你什么時間處理對象都會核查對象的是否釋放,
     //  check to see if it has been disposed.
     public   void  DoSomething()
       {
        if ( this .disposed)
          {
           throw   new  ObjectDisposedException();
       }
    }
    對于需要調用Close方法比Dispose方法更加自然的類型,可以在  基類增加一個Close方法。
   Close方法無參調用執行恰當清理工作的Dispose方法。
   下面的例子演示了Close方法。
     //  不要設置方法為virtual.
     //  繼承類不允許重寫這個方法
     public   void  Close()
       {
     //  無參數調用Dispose參數.
        Dispose();
    }

     public   static   void  Main()
       {
          //  Insert code here to create
          //  and use a BaseResource object.
    }

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 满城县| 芦溪县| 莆田市| 和顺县| 张掖市| 剑河县| 寿宁县| 昌邑市| 桦川县| 黔西县| 南和县| 浮梁县| 蕲春县| 奎屯市| 卢氏县| 花莲市| 平度市| 浦北县| 云安县| 张家界市| 青铜峡市| 潼关县| 大余县| 左权县| 扬中市| 九龙县| 旌德县| 黔西县| 那曲县| 塔城市| 柳林县| 漳州市| 绥芬河市| 萨嘎县| 邹城市| 龙南县| 呼伦贝尔市| 准格尔旗| 稷山县| 冷水江市| 于田县|