.NET的GC机制

来源:互联网 发布:淘宝网安卓版下载安装 编辑:程序博客网 时间:2024/06/06 02:23

.NET的GC机制有这样两个问题:

首先,GC并不是能释放所有的资源。它不能自动释放非托管资源。

第二,GC并不是实时性的,这将会造成系统性能上的瓶颈和不确定性。

垃圾回收的优缺点

实现垃圾回收有多种方法,每一种方法都提供了不同的性能特征。然而,所有的垃圾回收系统都具有一个共同的、与手工方法相对的属性。垃圾回收最主要的优点是简单和安全。
在垃圾回收环境中,可以显式地使用new分配内存,但是不需要显式地释放内存。相反,不再使用的内存会被自动回收。因此,不可能会忘记释放对象或者过早地释放对象。
这样做简化了程序设计,并且阻止了有问题的类。另外,不可能会意外地两次释放动态分配的内存。因此,垃圾回收为内存管理问题 提供了一种易于使用的、不容易犯错的、
可靠的解决方案。

遗憾的是,垃圾回收的简单及安全性是有代价的。第一个代价是垃圾回收机制引起的开销。所有垃圾回收的配置都会消耗一些CPU资源,因为这种不再使用的内存的回收并不是
一个免费过程。当使用手工方法的时候,不会有这样的开销。

第二个代价是在销毁对象时容易失控。使用手工方法时,当对对象执行delete语句的时候,及时地销毁这个对象(和所调用的它的析构函数),而垃圾回收没有这种切实而快速
的规则。相反,当使用垃圾回收时,直到回收器运行并回收对象的时候,对象才会被销毁,而回收器只有在某个特定时刻才会运行。例如, 回收器只有在自由内存的数量低于
某个值的时候才会运行。另外,用户并不能总是知道垃圾回收器销毁对象的顺序和时间。在某些情况下,不能准确地知道对象销毁 的时间会导致一些问题,因为这意味着程序
不能准确地知道何时为动态分配的对象调用析构函数。

对于作为后台任务运行的垃圾回收系统,这种失控可能会引发某种应用程序潜在的更加严重的问题,因为这样做将某种本质上不确定的行为引入到程序中。在 后台运行的垃圾
回收器实际上在不可预知的某个时刻回收不再使用的内存。例如,回收器通常只有在CPU空闲的时候才会运行。由于可能从一个程序的运行转到下 一个程序,从一台计算机转到
下一台计算机,或者从一个操作系统转到另一个操作系统,因此垃圾回收器在程序中执行的确切位置是不能确定的。对于许多应用程序而言,这并不存在问题,但是对于实时
应用程序这可能会引发灾难,因为在实时应用程序中对垃圾回收器不可预知的CPU循环的分配会导致事件的丢失。

 

为了解决第一个问题,.NET提供了析构函数,在C#中是~ClassName的形式。如果某个类定义了析构函数,.NET会在第一次的GC中调用析构函数,第二次才真正进行资源释放。
这就允许了我们能够做一些手动的资源管理操作,手动对非托管资源进行清理。但是如果没有必要,定义析构函数就会对性能造成较大的影响。

仅仅依赖析构函数对非托管资源进行释放是不够的,这是由于第二个问题:GC并不是实时性的,这会造成系统性能上的瓶颈和不确定性。所以有了IDisposable接口,
IDisposable接口定义了Dispose方法,这个方法用来供程序员显式调用以释放非托管资源。

通常我们应该这样写程序:

        public class SampleClass : System.IDisposable

        {

                public void Dispose()

                //供程序员显式调用的Dispose方法

                {

                        Dispose(true);

                        //调用带参数的Dispose方法,释放托管和非托管资源

                        System.GC.SuppressFinalize(this);

                        //手动调用了Dispose释放资源,那么析构函数就是不必要的了,这里阻止GC调用析构函数

                }

                protected void Dispose(bool disposing)

                //protected的Dispose方法,保证不会被外部调用。

                //传入bool值disposing以确定是否释放托管资源

                {

                        if (disposing)

                        {

                                //在这里加入清理"托管资源"的代码,应该是xxx.Dispose();

                        }

                        // 在这里加入清理"非托管资源"的代码

                }

                ~SampleClass()

                //供GC调用的析构函数

                {

                        Dispose(false);

                        //释放非托管资源

                }

        }

        这样一来,我们就像Delphi里调用Object.Free方法一样自然的调用Object.Dispose方法,而即使我们忘记了在合适的时候调用Dispose,GC也会在释放对象的时候帮
我们清理非托管资源的。GC所充当的角色只是一种保障手段,它应该充当这种角色,我们不能过分依赖它。

        实际上,在较大的模块退出时我们还应该及时地手动调用GC.Collect进行垃圾回收。
  GC.Collect();
  GC.WaitForPendingFinalizers();

原创粉丝点击