.NET Framework 自动内存管理机制深入剖析 (C#分析篇)

原创 2002年11月21日 09:50:00

在.NET Framework中,内存中的资源(即所有二进制信息的集合)分为"托管资源"和"非托管资源".托管资源必须接受.NET Framework的CLR(通用语言运行时)的管理(诸如内存类型安全性检查),而非托管资源则不必接受.NET Framework的CLR管理.  (了解更多区别请参阅.NET Framework或C#的高级编程资料)

托管资源在.NET Framework中又分别存放在两种地方: "堆栈"和"托管堆"(以下简称"堆");规则是,所有的值类型(包括引用和对象实例)和引用类型的引用都存放在"堆栈"中,而所有引用所代表的对象实例都保存在堆中.

在C#中,释放托管资源是可以自动通过"垃圾回收器"完成的(注意,"垃圾回收"机制是.NET Framework的特性,而不是C#的),但具体来说,仍有些需要注意的地方:
  1.值类型(包括引用和对象实例)和引用类型的引用其实是不需要什么"垃圾回收器"来释放内存的,因为当它们出了作用域后会自动释放所占内存(因为它们都保存在"堆栈"中,学过数据结构可知这是一种先进后出的结构);
  2.只有引用类型的引用所指向的对象实例才保存在"堆"中,而堆因为是一个自由存储空间,所以它并没有像"堆栈"那样有生存期("堆栈"的元素弹出后就代表生存期结束,也就代表释放了内存),并且非常要注意的是,"垃圾回收器"只对这块区域起作用;
  3."垃圾回收器"也许并不像许多人想象的一样会立即执行(当堆中的资源需要释放时),而是在引用类型的引用被删除和它在"堆"中的对象实例被删除中间有个间隔,为什么呢? 因为"垃圾回收器"的调用是比较消耗系统资源的,因此不可能经常被调用!

(当然,用户代码可以用方法System.GC.Collect()来强制执行"垃圾回收器")

然而,大多数情况下,我们需要明确地在不执行"垃圾回收器"的情况下释放托管资源(因为只需要释放一部分但又是非常需要释放的资源,但最好不要调用"垃圾回收器",因为"垃圾回收器"太浪费系统资源了),或需要释放"非托管资源",这时候我们该怎么办? 这是我们写代码的时候必须要考虑的问题("垃圾回收器"是系统自动实现的,一般情况不需要用户干预),否则Windows系统会因为内存耗尽而...
现在,我来告诉怎么办,那就是使用类的Dispose()方法释放所有类型资源 和 使用析构方法释放非托管资源!
1.Dispose()方法
  要通过Dispose()方法来释放资源,那么在类定义的时候执"System.IDisposable"接口,然后在类中必须包含这样定义的方法"void Dispose()" (在Dispose()方法中就是用户自己写的释放资源的代码段),这样一来,用户就会知道可以通过人为地调用Dispose()方法来释放资源. 不过需要注意的是,"垃圾回收器"并不是通过调用Dispose()方法来释放托管资源的!

2.析构方法
  在C#中定义析构方法的格式是" ~CLASS_NAME() ".非常需要注意的是,如果一个类中没有使用到非托管资源,那么请一定不要定义析构方法,这是因为对象执行了析构方法,那么"垃圾回收器"在释放托管资源之前要先调用析构方法,然后第二次才真正释放托管资源,这样一来,两次删除动作的花销比一次大多的! (不过,即使你在类中已经定义了析构方法,仍然有办法"屏蔽"它,这将在后面的代码范例中说明) 在析构方法中,就是用户自己写的释放非托管资源的代码段.

下面使用一段代码来示范Dispose()方法和析构方法如何使用:

   public class ResourceHolder : System.IDisposable
   {
      public void Dispose()
      {
         Dispose(true);
         System.GC.SuppressFinalize(this);
         // 上面一行代码作用是防止"垃圾回收器"调用这个类中的方法
         // " ~ResourceHolder() "
         // 为什么要防止呢? 因为如果用户记得调用Dispose()方法,那么
         // "垃圾回收器"就没有必要"多此一举"地再去释放一遍"非托管资源"了
         // 如果用户不记得调用呢,就让"垃圾回收器"帮我们去"多此一举"吧 ^_^
         // 你看不懂我上面说的不要紧,下面我还有更详细的解释呢!

      }

      protected virtual void Dispose(bool disposing)
      {
         if (disposing)
         {
          // 这里是清理"托管资源"的用户代码段
         }
        // 这里是清理"非托管资源"的用户代码段
      }

      ~ResourceHolder()
      {
         Dispose(false);
      }
   }

  上面的代码是一个典型的有两种Dispose方法的类定义.
   在.NET Framework中有很多系统类是用这种方法定义Dispose()方法的,例如:
   MSDN中,System.Drawing.Brush.Dispose方法就是这样定义的:
   ************************************************************
   * 释放由此 Brush 对象使用的所有资源。                      *
   * public void Dispose()                                    *
   * 该成员支持 .NET 框架结构,因此不适用于直接从代码中使用。 *
   * protected virtual void Dispose(bool);                    *
   ************************************************************

   这里,我们必须要清楚,需要用户调用的是方法Dispose()而不是方法Dispose(bool),然而,这里真正执行释放工作的方法却并不是Dispose(),而是Dispose(bool) ! 为什么呢?仔细看代码,在Dispose()中,调用了Dispose(true),而参数为"true"时,作用是清理所有的托管资源和非托管资源;大家一定还记得我前面才说过,"使用析构方法是用来释放非托管资源的",那么这里既然Dispose()可以完成释放非托管资源的工作,还要析构方法干什么呢? 其实,析构方法的作用仅仅是一个"备份"!
   为什么呢?
严格地说,凡执行了接口"IDisposable"的类,那么只要程序员在代码中使用了这个类的对象实例,那么早晚得调用这个类的Dispose()方法,同时,如果类中含有对非托管资源的使用,那么也必须释放非托管资源! 可惜,如果释放非托管资源的代码放在析构方法中(上面的例子对应的是 " ~ResourceHolder() "),那么程序员想调用这段释放代码是不可能做到的(因为析构方法不能被用户调用,只能被系统,确切说是"垃圾回收器"调用),所以大家应该知道为什么上面例子中"清理非托管资源的用户代码段"是在Dispose(bool)中,而不是~ResourceHolder()中! 不过不幸的是,并不是所有的程序员都时刻小心地记得调用Dispose()方法,万一程序员忘记调用此方法,托管资源当然没问题,早晚会有"垃圾回收器"来回收(只不过会推迟一会儿),那么非托管资源呢?它可不受CLR的控制啊!难道它所占用的非托管资源就永远不能释放了吗? 当然不是!我们还有"析构方法"呢! 如果忘记调用Dispose(),那么"垃圾回收器"也会调用"析构方法"来释放非托管资源的!(多说一句废话,如果程序员记得调用Dispose()的话,那么代码"System.GC.SuppressFinalize(this);"则可以防止"垃圾回收器"调用析构方法,这样就不必多释放一次"非托管资源"了) 所以我们就不怕程序员忘记调用Dispose()方法了.
  所以我说了这么一大堆的理由,综合起来只有两点:
  *1.程序员们啊,千万不要忘记调用Dispose()方法! (如果有的话 ^_^)
  *2.万一忘记,不要着急...还有救!!! 因为还有"垃圾回收器"帮我们自动调用析构方法!

FreeRTOS代码剖析之1:内存管理Heap_1.c

内存管理是一个操作系统的重要组成部分之一,所有应用程序都离不开操作系统的内存管理。因此,在剖析FreeRTOS的内核代码之前,前对FreeRTOS的内存管理进行研究。     现在以FreeRTOS...
  • u014381531
  • u014381531
  • 2014年08月16日 23:58
  • 2493

Android进程管理机制及优化+自动内存管理程序

理论 Android采取了一种有别于Linux的进程管理策略,有别于Linux的在进程活动停止后就结束该进程,Android把这些进程都保留在内存中,直到系统需要更多内存为止。这些保留在内存中的进程...
  • lcl_01101
  • lcl_01101
  • 2015年05月14日 17:41
  • 1060

深入理解Linux内存管理机制

深入理解Linux内存管理机制(一) 浏览次数:595次2012年08月03日淘宝核心系统团队博客字号: 大 中 小 分享到:QQ空间新浪微博腾讯微博人人网豆瓣网开心网更多1 深...
  • haiross
  • haiross
  • 2014年08月29日 10:45
  • 1973

.NET Framework 自动内存管理机制深入剖析 (C#分析篇)

在.NET Framework中,内存中的资源(即所有二进制信息的集合)分为”托管资源”和”非托管资源”.托管资源必须接受.NET Framework的CLR(通用语言运行时)的管理(诸如内存类型安全...
  • qq_33632356
  • qq_33632356
  • 2017年12月20日 22:49
  • 37

C#的自动内存管理机制-ASP.NET中的垃圾回收机制(GC)【资料来自网络】

ASP.NET中的垃圾回收机制(GC)【资料来自网络】 ASP.NET中的垃圾回收机制: 在说这个问题的时候,我们先引入一个在GC中比较常见的内容,那就是--托管资源,那么什么是托管资源呢?其...
  • zhangys7981
  • zhangys7981
  • 2011年09月19日 15:30
  • 616

C#的数据类型以及内存管理机制剖析(2)

1. Object类再分析: System.Object是所有.Net类的基类,包括值类型和引用类型。值类型为什么也是继承于System.Object的呢?Object不是引用类型吗?这个就涉及了....
  • tmackan
  • tmackan
  • 2013年05月14日 15:46
  • 654

C#的数据类型以及内存管理机制剖析(1)

尽管C#(事实上是基于.Net Framework的所有语言)自动处理了内存的分配和释放的问题,并且引入了垃圾收集机制,有完善的数据类型管理能力。但是对于很多情况下,了解其深层的机制是非常有用的,能够...
  • csndcsndwei
  • csndcsndwei
  • 2012年05月02日 21:52
  • 258

C#的数据类型以及内存管理机制剖析(2)

1. Object类再分析: System.Object是所有.Net类的基类,包括值类型和引用类型。值类型为什么也是继承于System.Object的呢?Object不是引用类型吗?这个就涉及了....
  • csndcsndwei
  • csndcsndwei
  • 2012年05月02日 21:53
  • 208

《Effective C#》读书笔记——了解.NET内存管理机制<.NET资源管理>

我们知道C#是一门虚拟机语言,在C#编译器首先将C#代码编译成IL代码,运行程序时CLR(Common Language Runtime,公共语言运行时)通过调用JIT(just-in-time Co...
  • sundaydashu
  • sundaydashu
  • 2012年10月22日 11:34
  • 337

.NET framework的内存管理和垃圾回收

尽管在.NET framework下我们并不需要担心内存管理和垃圾回收(Garbage Collection),但是我们还是应该了解它们,以优化我们的应用程序。同时,还需要具备一些基础的内存管理工作机...
  • binbingg
  • binbingg
  • 2013年08月28日 13:40
  • 748
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:.NET Framework 自动内存管理机制深入剖析 (C#分析篇)
举报原因:
原因补充:

(最多只允许输入30个字)