链接

  1. 对一些非托管的资源,如文件句柄、数据库连接、GDI+对象,COM对象以及其他一些系统级别的对象进行访问后,我们需要进行手动的清理。

  2. 有时候可能会使得某些对象在内存中的存留时间比我们预期的要长,比如在我们创建事件或者委托的时候。

  3. 一些查询表达式,因为有延迟执行的特性,会使得一些对象的生存期会比我们预想的要长。查询表达式会捕获闭包中的局部变量,这些变量需要等到我们离开调用代码域之后,才能被释放。

  4. GC工作在自己专有的线程里面,他会为我们回收不需要的内存。并且会对托管推进行压缩,压缩的过程中会涉及到对象的移动,以使得剩余的空间在内存中连续排列,以提高效率和节省空间。

  5. GC会调用方法的终结器(finalizers),我们不知道什么时候被调用,只知道在对象不可达变为垃圾之后某个时候会被调用。
  6. 在.NET框架中,对非托管资源的释放有一套标准的模式。在我们编写自己的对象时也应该遵循这一模式。标准的释放非托管代码的模式是:如果用户记得,那么就会调用IDisposable接口,如果用户忘记了就防御性的使用终结器操作。这是处理非托管资源的正确方式。

  7. 线程这个东西始终要自己控制,不要指望主进程会正确关闭它们
  8. 劲量不手动控制GC,效果不是很好的(微软对GC的解释是:我们也不知道他什么时候会释放什么)
  9. 首先,如果你的类使用到了非托管方法,那么就需要一个终结器,我们不能够指望用户总是会调用Dispose方法,当用户忘记调用时,如果没有终结器的话,就会发生内存泄露。他们忘记调用Dispose是他们的问题,但是我们却需要承担责任。唯一能够保证非托管资源能够有效释放的操作是创建一个终结器。
  10. 在垃圾收集器工作时,它会立即移除内存中的那些没有终结器的垃圾对象。所有具有终结器的对象会留存在内存中。这些对象会添加到终结队列中,然后垃圾收集器会触发另外一个新的线程来执行这些对象的终结操作,在终结操作线程执行完成之后,垃圾收集器就可以将这些对象从内存中移除了。拥有终结操作的对象比没有改操作的对象在内存中会存续更多的时间。但是如果防御性编程中,如果我们的类型中使用到了非托管资源,我们必须这样做。现在先不要担心性能问题。后面我们会看到在使用终结器的时候,如何避免性能损耗。
实现我们自己的IDisposable.Dispose() 方法主要执行以下任务:
1. 释放所有的非托管资源
2. 释放所有的托管资源,包括未注册的事件
3. 设定一个标志位来标识对象已经被释放。我们需要检查这个标志位,然后如果在对象释放后再调用这个方法,需要抛出ObjectDisposed异常。
4. 阻止终结操作(suppressing finalization),我们可以调用GC.SuppressFinalize(this)来完成该操作。
public void FlushMemory()
{
    GC.Collect();
    GC.WaitForPendingFinalizers();
    if (Environment.OSVersion.Platform == PlatformID.Win32NT)
    {
        SetProcessWorkingSetSize(System.Diagnostics.Process.GetCurrentProcess().Handle, -1, -1);
    }
}

protected virtual void Dispose(bool isDisposing) 当isDisposing对象为true的时候,清理托管和非托管资源,当isDisposing为false的时候,只清理非托管资源。

因为Dispose()方法可能会被调用多次。如果某个对象中,该方法已经调用过一次,那么第二次调用的时候,应该什么都不做。

.NET框架类库(FCL)提供了实现手动清理 非托管资源的基本框架,这就是Dispose模式。

  • 托管代码是指用.net framework支持的语言写的代码
  • 非托管代码是在公共语言运行库环境的外部,由操作系统直接执行的代码。非托管代码必须提供自己的垃圾回收、类型检查、安全支持等服务

  • 托管代码的堆上的垃圾内存可由GC自动回收,非托管代码中堆上的垃圾内存必须程序员自己负责释放、回收。
  • 在.NET的对象中实际上有两个用于释放资源的函数:Dispose和Finalize。
  • Finalize的目的是用于释放非托管的资源,而Dispose是用于释放所有资源,包括托管的和非托管的

托管与非托管代码

msdn Managed code is code written in one of over twenty high-level programming languages that are available for use with the Microsoft .NET Framework, including C#, J#, Microsoft Visual Basic .NET, Microsoft JScript .NET, and C++. All of these languages share a unified set of class libraries and can be encoded into an Intermediate Language (IL). A runtime-aware compiler compiles the IL into native executable code within a managed execution environment that ensures type safety, array bound and index checking, exception handling, and garbage collection.

By using managed code and compiling in this managed execution environment, you can avoid many typical programming mistakes that lead to security holes and unstable applications. Also, many unproductive programming tasks are automatically taken care of, such as type safety checking, memory management, and destruction of unneeded objects. You can therefore focus on the business logic of your applications and write them using fewer lines of code. The result is shorter development time and more secure and stable applications.

那么.Net如何判定一个引用类型对象是垃圾呢,.Net的判断很简单,只要判定此对象或者其包含的子对象没有任何引用是有效的,那么系统就认为它是垃圾。 对于内存中的垃圾分为两种,一种是需要调用对象的析构函数,另一种是不需要调用的。 GC对于前者的回收需要通过两步完成,第一步是调用对象的析构函数,第二步是回收内存,但是要注意这两步不是在GC一次轮循完成,即需要两次轮循;相对于后者,则只是回收内存而已。

经过前面的介绍,可以知道析构函数只能被GC来调用的,那么无法确定它什么时候被调用,因此用它作为资源的释放并不是很合理,因为资源释放不及时;但是为了防止资源泄漏,毕竟它会被GC调用,因此析构函数可以作为一个补救方法。而Close与Dispose这两种方法的区别在于,调用完了对象的Close方法后,此对象有可能被重新进行使用;而Dispose方法来说,此对象所占有的资源需要被标记为无用了,也就是此对象被销毁了,不能再被使用

对于托管程序来说,非托管资源来说,需要手动来释放,否则会造成资源泄漏,如果放到析构函数去完成,但是GC调用对象的析构函数时机并不确定,对于资源释放并不及时。换句话说析构函数只是保障资源不被泄漏的方法,但不是最好的方法。比较好的方法,就是使用.Net的IDisposable接口提供的Dispose方法来进行释放。 那么对于IDisposable接口的Dispose函数,需要做些什么呢。大致有如下四点。

  1. 释放所有非托管资源;
  2. 释放所有托管资源,这包括取消事件绑定;
  3. 设置标志位,标明对象已经释放;
  4. 调用“GC.SuppressFinalize(this)”来避免GC再去掉用对象的析构函数。