C#垃圾回收與記憶體管理【轉】
本節的組織如下,1..Net的型別和記憶體分配2.GC垃圾收集器的工作原理3.什麼是非託管資源4.如何有效釋放物件資源。總結.現在開始我們本節的學習。
1..Net的型別和記憶體分配
Net中的所有型別都是(直接或間接)從 System.Object 型別派生的。
CTS 中的型別被分成兩大類——引用型別( reference type ,又叫託管型別 [managed type] ),分配在記憶體堆上,值型別(
值型別在棧裡,先進後出,值型別變數的生命有先後順序,這個確保了值型別變數在推出作用域以前會釋放資源。比引用型別更簡單和高效。堆疊是從高地址往低地址分配記憶體。
引用型別分配在託管堆(Managed Heap)上,宣告一個變數在棧上儲存,當使用new建立物件時,會把物件的地址儲存在這個變數裡。託管堆相反,從低地址往高地址分配記憶體,如圖
2.GC垃圾收集器的工作原理
上圖中,當dataSet使用過期以後,我們不顯示銷燬物件,堆上的物件還繼續存在,等待GC的 回收。
垃圾收集器通過分代支援物件的年齡化是推薦的但不是必需的。一代在記憶體裡是一個具有相對年齡的物件的單位。物件的
代號或年齡標識物件屬於那個分代。在應用程式的生命週期裡,越近建立的物件屬於越新的代,並且比早建立的物件具有
較低的分代號。最近分代裡的物件代號是0.
在new物件時,要先搜尋空閒連結串列,找到最適合記憶體塊,分配,調整記憶體塊連結串列,合併碎片。new操作幾乎可以在O(1)的時間完成,把堆頂指標加1。工作原理是: 當託管堆上剩餘空間不足,或者Generator 0 的空間已滿的時候GC執行,開始回收記憶體。垃圾回收的開始,GC對堆記憶體的壓縮調整,物件集中到頂部。GC在掃描垃圾的時候會佔用一定的CPU時間片的,最初的GC演算法真的是掃描整個堆,效率低。現在的GC把堆中的物件分成3代,最近
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(定義了解構函式)。
具體程式碼如下:
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 垃圾收集器。可以根據策略在需要的時候回收託管資源,
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 的例項。建立的客戶端進行了簡單的測試。客戶端程式碼如下:
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