在这里,对象可以通过两种方式被清除。第一种方式是通过 IDisposable 接口的 Dispose 方法。此方法在对象显式地结束时被客户代码调用,它调用 InternalD" name="description" />

深入.NET托管堆(managedheap)(下)

发表于:2007-05-25来源:作者:点击数: 标签:managedheap宋体.NET深入托管
MI LY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在这里,对象可以通过两种方式被清除。第一种方式是通过 IDisposable 接口的 Dispose 方法。此方法在对象显式地结束时被客户代码调用,它调用 InternalD

MILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">在这里,对象可以通过两种方式被清除。第一种方式是通过IDisposable接口的Dispose方法。此方法在对象显式地结束时被客户代码调用,它调用InternalDispose(true)。在这种情况下所有的对象都被清除了。如果析构函数被调用,那么InternalDispose(false)被调用,此时只有外部资源会被释放。如果我们已经执行了终止操作,那么我们自己的对象有可能已经被释放了,此后对它们的引用有可能引起异常。

 

GC.SuppressFinalize的调用会阻止垃圾收集器将对象放入终止队列中。这样做可以降低在一次GC过程中由于整理对象而引起的内存消耗,并且由于终止操作不会被调用,从而使性能得到提高。

 

C#的优化

 

因此使用IDisposable.Dispose()来释放资源是个很好的方式,它不但可以减少一些在托管堆上进行操作的内存需求,而且能够减少必须执行终止操作的对象的数量。但是它使用起来比较麻烦,尤其是有多个临时对象被创建的时候更是如此。为了能够从IDisposable接口受益,C#客户程序应该书写象下面这样的代码:

 

OverdueBookLocator bookLocator = null;

try

{

    bookLocator = new OverdueBookLocator();

    // Use bookLocator here

    Book book = bookLocator.Find("Eiffel, the Language");

    .

    .

    .

}

finally

{

    if(bookLocator != null)

    {

        IDisposable disp = bookLocator as IDisposable;

        disp.Dispose();

    }

}

 

finally中的代码被用来在有异常发生时作适当的清理工作。为了C#客户程序能够简单有效地使用Dispose模式,Beta2引入了using表达式。Using表达式允许你简化你的代码,因此上面的代码可以写成:

 

 

using(bookLocator = new OverdueBookLocator())

{

   // Use bookLocator here

   Book book = bookLocator.Find("Eiffel, the Language");

}

 

无论何时分配具有明确定义的生存期的类型时,你都应该使用using表达式。它能保证对IDisposable接口的适当调用,即使是在有异常发生的时候。

 

使用System.GC

 

System.GC类用来访问被.NET framework暴露出来的垃圾回收机制。这个类包含以下一些有用的方法:

 

     GC.SuppressFinalize 这个方法在前面已经描述过了,它能够抑制终止操作。如果你已经将属于一个对象的外部资源释放了,调用这个方法来抑制此对象的终止操作的执行。

     GC.Collect 具有两个版本。不带参数的版本在托管堆的所有generation上执行回收动作。另一个版本带有一个整型参数,此参数指明所要进行回收操作的generation。你将很少调用这个方法,因为垃圾收集器在需要的时候会自动调用它。

     GC.GetGeneration 返回作为参数传入的对象所在的generation。这个方法在由于性能的原因而进行的调试和跟踪中很有作用,但是在大部分应用中作用有限。

     GC.GetTotalMemory 返回堆中已经被分配的内存总量。由于托管堆的工作方式,这个数字并不精确,但是如果你以true作为参数的话,还是会得到一个比较接近的近似值。这个方法在计算之前会先执行一遍回收操作。

 

下面是使用这些方法的一个例子:

 

/// <summary>

/// Displays current GC information

/// </summary>

/// <param name="generation">The generation to collect</param>

/// <param name="waitForGC">Run GC before calculating usage?</param>

public void CollectAndAudit(int generation, bool waitForGC)

{

  int myGeneration = GC.GetGeneration(this);

  long totalMemory = GC.GetTotalMemory(waitForGC);

  Console.WriteLine("I am in generation {0}.", myGeneration);

  Console.WriteLine("Memory before collection {0}.", totalMemory);

  GC.Collect(generation);

  Console.WriteLine("Memory after collection {0}.", totalMemory);

}

 

关于本文作者

 

Mickey WilliamsCodev Technologies的创始人之一。Codev Technologies是一家从事位Windows程序开发者提供咨询和工具的机构。他同时也是.NET Experts (http://www.codeguru.com/columns/DotNet/www.dotnetexperts.com)的主要成员,他在此讲授.NET Framework的课程。他时常在美国和欧洲的一些研讨会上发表演讲,并且已经写了八本有关Windows程序设计方面的著作。他目前正被微软出版社邀请写作“Microsoft Visual C#”。你可以在mw@codevtech.com找到他。

原文转自:http://www.ltesting.net

评论列表(网友评论仅供网友表达个人看法,并不表明本站同意其观点或证实其描述)