您的位置:首页 > 编程语言 > C#

理解C#垃圾回收机制

2009-07-30 17:58 453 查看
很多系统都有其自身的垃圾回收,其回收机制大体是相同的。它们使程序员从跟踪内存使用的繁重任务中解脱出来。虽然大多数回收器都要求应用程序不时地暂停从而释放不再使用的内存。但C#中的回收器效率还是很高的。
垃圾回收器的基本假定:
1.被分配内存空间的对象最有可能被释放。在方法执行时,就需要为该方法的对象分配内存空间,搜索最近分配的对象集合有助于花费最少的代价来尽可能多地释放内存空间。
2.生命期最长的对象释放的可能性最小,经过几轮垃圾回收后,对象仍然存在,搜索它时就需要进行大量的工作,却只能释放很小的一部分空间。
3.同时被分配内存的对象通常是同时使用,将它们彼此相连有助于提高缓存性能和回收效率 。
C#中的回收器是分代的垃圾回收器(Gererational Garbage Collector) 它将分配的对象分为3个类别或代。(可用GC.GetGeneration方法返回任意作为参数的对象当前所处的代) 最近被分配内存的对象被放置于第0代,因为第0代很小,小到足以放进处理器的二级(L2)缓存,所以它能够提供对对象 的快速存取。经过一轮垃圾回收后,仍然保留在第0代中的对象被移进第1代中,再经过一轮垃圾内存回收后,仍然保留在第1代中的对象则被移进第2代中,第2代中包含了生存期较长的对象。
在C#中值类型是在堆栈中分配内存,它们有自身的生命周期,所以不用对它们进行管理,会自动分配和释放。而引用类型是在堆中分配内存的。所以它的分配和释放就需要像回收机制来管理。C#为一个对象分配内存时,托管堆可以立即返回新对象所需的内存,因为托管堆类似于简单的字节数组,有一个指向第一个可用内存空间的指针,指针像游标一样向后移动,一段段内存就分配给了正在运行的程序的对象。在不需要太多垃圾回收的程序小,托管堆性能优于传统的堆。
当第0代中没有可以分配的有效内存时,就触发了第0代中的一轮垃圾回收,它将删除那些不再被引用的对象,并将当前正在使用的对象移至第1代。而当第0代垃圾回收后依然不能请求到充足的内存时,就启动第1代垃圾回收。如果对各代都进行了垃圾回收后仍没有可用的内存就会引发一个OutOfMemoryException异常。

终结器
在有些情况下,类可以提供一个终结器在对象被销毁时执行,终结器是一个名为Finalize的受保护的方法:

protected void Finalize()
{
base.Finalize();
//释放外部资源
}

垃圾回收器使用名为“终止队列”的内部结构跟踪具有 Finalize 方法的对象。每次您的应用程序创建具有 Finalize 方法的对象时,垃圾回收器都在终止队列中放置一个指向该对象的项。托管堆中所有需要在垃圾回收器回收其内存之前调用它们的终止代码的对象都在终止队列中含有项。(实现 Finalize 方法或析构函数对性能可能会有负面影响,因此应避免不必要地使用它们。用 Finalize 方法回收对象使用的内存需要至少两次垃圾回收。当垃圾回收器执行回收时,它只回收没有终结器的不可访问对象的内存。这时,它不能回收具有终结器的不可访问对象。它改为将这些对象的项从终止队列中移除并将它们放置在标为准备终止的对象列表中。该列表中的项指向托管堆中准备被调用其终止代码的对象。垃圾回收器为此列表中的对象调用 Finalize 方法,然后,将这些项从列表中移除。后来的垃圾回收将确定终止的对象确实是垃圾,因为标为准备终止对象的列表中的项不再指向它们。在后来的垃圾回收中,实际上回收了对象的内存。)
Dispose方法
在不使用终结器时,可以考虑使用Dispose方法,你可以使用这个方法来释放所保存包括的在托管对象引用在内的任何资源。但使用它时需用GC.SuppressFinalize来告知运行时这些对象不需要终结。如下所示:

public void Dispose()
{
object.Dispose();
dbConnection.Dispose();
GC.SuppressFinalize(this); //申明不需要终结
}

创建并使用了Dispose方法的对象,就需要使用完该对象之后调用这些方法,最好是在Finally中调用。



1//以下代码演示来自MSDN
2// Design pattern for the base class.
3// By implementing IDisposable, you are announcing that instances
4// of this type allocate scarce resources.
5public class BaseResource: IDisposable
6{
7 // Pointer to an external unmanaged resource.
8 private IntPtr handle;
9 // Other managed resource this class uses.
10 private Component Components;
11 // Track whether Dispose has been called.
12 private bool disposed = false;
13
14 // Constructor for the BaseResource object.
15 public BaseResource()
16 {
17 // Insert appropriate constructor code here.
18 }
19
20 // Implement IDisposable.
21 // Do not make this method virtual.
22 // A derived class should not be able to override this method.
23 public void Dispose()
24 {
25 Dispose(true);
26 // Take yourself off the Finalization queue
27 // to prevent finalization code for this object
28 // from executing a second time.
29 GC.SuppressFinalize(this);
30 }
31
32 // Dispose(bool disposing) executes in two distinct scenarios.
33 // If disposing equals true, the method has been called directly
34 // or indirectly by a user's code. Managed and unmanaged resources
35 // can be disposed.
36 // If disposing equals false, the method has been called by the
37 // runtime from inside the finalizer and you should not reference
38 // other objects. Only unmanaged resources can be disposed.
39 protected virtual void Dispose(bool disposing)
40 {
41 // Check to see if Dispose has already been called.
42 if(!this.disposed)
43 {
44 // If disposing equals true, dispose all managed
45 // and unmanaged resources.
46 if(disposing)
47 {
48 // Dispose managed resources.
49 Components.Dispose();
50 }
51 // Release unmanaged resources. If disposing is false,
52 // only the following code is executed.
53 CloseHandle(handle);
54 handle = IntPtr.Zero;
55 // Note that this is not thread safe.
56 // Another thread could start disposing the object
57 // after the managed resources are disposed,
58 // but before the disposed flag is set to true.
59 // If thread safety is necessary, it must be
60 // implemented by the client.
61
62 }
63 disposed = true;
64 }
65
66 // Use C# destructor syntax for finalization code.
67 // This destructor will run only if the Dispose method
68 // does not get called.
69 // It gives your base class the opportunity to finalize.
70 // Do not provide destructors in types derived from this class.
71 ~BaseResource()
72 {
73 // Do not re-create Dispose clean-up code here.
74 // Calling Dispose(false) is optimal in terms of
75 // readability and maintainability.
76 Dispose(false);
77 }
78
79 // Allow your Dispose method to be called multiple times,
80 // but throw an exception if the object has been disposed.
81 // Whenever you do something with this class,
82 // check to see if it has been disposed.
83 public void DoSomething()
84 {
85 if(this.disposed)
86 {
87 throw new ObjectDisposedException();
88 }
89 }
90}
91
92// Design pattern for a derived class.
93// Note that this derived class inherently implements the
94// IDisposable interface because it is implemented in the base class.
95public class MyResourceWrapper: BaseResource
96{
97 // A managed resource that you add in this derived class.
98 private ManagedResource addedManaged;
99 // A native unmanaged resource that you add in this derived class.
100 private NativeResource addedNative;
101 private bool disposed = false;
102
103 // Constructor for this object.
104 public MyResourceWrapper()
105 {
106 // Insert appropriate constructor code here.
107 }
108
109 protected override void Dispose(bool disposing)
110 {
111 if(!this.disposed)
112 {
113 try
114 {
115 if(disposing)
116 {
117 // Release the managed resources you added in
118 // this derived class here.
119 addedManaged.Dispose();
120 }
121 // Release the native unmanaged resources you added
122 // in this derived class here.
123 CloseHandle(addedNative);
124 this.disposed = true;
125 }
126 finally
127 {
128 // Call Dispose on your base class.
129 base.Dispose(disposing);
130 }
131 }
132 }
133}
134
135// This derived class does not have a Finalize method
136// or a Dispose method without parameters because it inherits
137// them from the base class.
138

System.GC类
GC类包含了可使用户与垃圾回收机制进行互操作的静态方法,包括发起新一轮垃圾回收操作的方法。确定某对象当前所在代的方法及当前分配内存空间的方法。
GC.Collect(); //无参时将发起一轮全面的回收。
GC.Collect(i);//(0<=i<=2)对第i代进行垃圾回收。
GetTotalMemory将返因分配于托管堆上的内存空间总量。当参数为True时,在计算之前将进行一轮全面的垃圾回收。如下所示:

long totalMemory = System.GC.GetTotalMemory(True);
下面是 在.NET Framework 2.0 版中是新增的公共方法:
通知运行库在安排垃圾回收时应考虑分配大量的非托管内

public static void AddMemoryPressure (    long bytesAllocated)  //bytesAllocated
已分配的非托管内存的增量。

返回已经对对象的指定代进行的垃圾回收次数。

public static int CollectionCount (
int generation
)

通知运行库已释放非托管内存,在安排垃圾回收时不需要再考虑它。

public static void RemoveMemoryPressure (
long bytesAllocated
)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: