第十一章 管理类型(In .net4.5) 之 管理对象的生命周期

1. 概述

  本章内容包括 管理非托管资源、使用IDisposable接口 以及 管理析构器和垃圾回收。

2. 主要内容

  2.1 理解垃圾回收机制

    ① 代码执行的时候,内存中有两个地方存放数据项:堆 和 栈。

    ② 一个方法结束的时候,其使用的栈空间会被自动清空。 而堆空间,是由垃圾回收器管理的。

    ③ 垃圾回收器的工作原理是:启动以后,垃圾回收器的标记程序会遍历堆上保存的对象,标记出仍然被引用的对象,然后压缩程序启动,它会把当前仍然存在引用的对象移动到一起,然后释放掉其他不存在引用的对象。

    ④ 执行垃圾回收期间,为了确保对象状态的准确性,系统会暂停其他所有线程的执行,直到垃圾回收执行完毕。这可能会导致一定的程序响应问题。

    ⑤ 为了解决上述问题,垃圾回收器被设计成智能的。它会尽量在堆空间不足或内存不足的时候启动,而且尽量会在程序使用率低的时候启动。

    ⑥ 执行标记程序的时候,首次遍历到的对象默认是Generation 0,如果检测到该对象存在引用,则会提高该对象的Generation。其中根据的原则是:长时间停留的对象,可能会停留较长时间。所以这些对象仅会在空间不足的时候才会去检测和释放。

  2.2 管理非托管资源

    不涉及非托管资源的时候,基本无需考虑内存及资源管理问题,垃圾回收器都能处理好。但是一旦涉及到非托管资源,垃圾回收器就无能为力了,这时候就需要手动释放资源。

    析构器(finalizer)是释放资源的一种方式,但是C#中,析构器的执行时间是不确定的,是由垃圾回收器的算法决定的。但是可以通过调用GC.Collect来强制执行析构器。(这种做法是不推荐的)

StreamWriter sw = File.CreateFile("temp.dat");
sw.Write("some data");
GC.Collect();
GC.WaitForPendingFinalizers();
File.Delete("temp.dat");

   析构器延长了对象的生命周期。因为析构器总要执行,.net平台会在一个特殊的析构队列中保存一个该对象的引用。这会推迟垃圾回收器的回收时间。

    综上所述,对于释放非托管资源,析构器不是一个很好的方案。.net有更好的推荐:IDisposable。

using(StreamWriter stream = File.CreateText(“temp.dat”))
{   stream.Write(“some data”);   stream.Dispose();   File.Delete(“temp.dat”);
}

    使用using,可以自动处理其中代码的异常情况,避免因为代码中的异常可能导致的资源没有释放的问题。

  2.3 实现IDisposable接口和析构器

    在自定义类型中同时实现IDisposable接口和析构器,是一个必要的方案。可以避免用户忘记调用Dispose方法的情况。

class UnmanagedWrapper : IDisposable
{
    public FileStream Stream { get; private set;}

    public UnmananagedWrapper()
    {
        this.Stream = File.Open("temp.dat", FileMode.Create);
    }

    ~UnmanagedWrapper()
    {
        Dispose(false);
    }

    public void Close()
    {
        Dispose();
    }

    public void Dispose()
    {
        Dispose(true);
        System.GC.SuppressFinalizer(this);
    }

    public void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (Stream != null)
                Stream.Close();
        }
    }
}        

    * System.GC.SuppressFinalizer(object obj)会请求系统不要调用指定对象的析构器。

  2.4 弱引用(weak references)

static WeakReference data;
public  static void Run()
{
    object result = GetData();
    //GC.Collect(); 取消这行注释将会导致data.Target为null
    result = getData();
} 
private static object GetData()
{
    if (data == null)
        data = new WeakReference(LoadLargeList());

    if (data.Target == null)
        data.Target = LoadLargeList();

    return data.Garget;
}

   弱引用可用于缓存场景,用弱引用定义的对象,不会阻碍垃圾回收器的回收。上面的GetData方法,确保在对象被回收以后,重新获取并保存对象。

3. 总结

  ① C#中,用堆和栈在内存中保存数据项。堆空间是受垃圾回收器管理的。

  ② 垃圾回收器会释放堆上那些已经不存在引用的对象。

  ③ 析构器是类中一段特殊的代码,在类对象被删除的时候,由垃圾回收器负责调用。

  ④ IDisposable接口,可以实现用可控的方式来释放非托管资源。

  ⑤ 可以用using关键字来确保实现了IDisposable的对象总会被成功释放。

  ⑥ 弱引用 可以保存一个对象,该对象会被垃圾回收器当做已经没有引用的对象而回收。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。