前言
GC(垃圾回收)实现的时候,对引用资源管理都是对托管资源的管理,而在c#中还有一些非托管资源,现在简单讲下,两者的区别,和对非托管资源是怎么释放内存的。
托管资源
.NET中的类型都是从System.Object类型派生出来的。
分为两大类:
引用类型(reference type),分配在内存堆上。
值类型(value type),分配在堆栈上。
分配内存
值类型在栈里,先进后出,值类型变量的生命有先有后,这个确保了值类型变量在退出作用域以前会释放资源。比引用类型更简单和高效。堆栈是从高地址往低地址分配内存。
引用类型分配在托管堆上,声明一个变量在栈上保存,当实例化一个对象的时候,会把对象的地址存储在这个变量里。托管堆相反,从低地址往高地址分配内存。
初始化新进程的时候,CLR会为进程保留一个连续的地址空间区域。这个保留的地址空间被称为托管堆。托管堆维护着一个指针,用它指向将在堆中分配的下一个对象的地址。一开始该指针指向托管堆的基址。托管堆上包含了所有的引用类型。从托管堆中分配内存要比非托管内存分配速度快。由于CLR通过为指针添加值来为对象分配内存,所以这几乎和从堆栈中分配内存一样快。另外由于连续分配的新对象在托管堆中是连续存储,所以应用程序可以快速访问这些对象。
释放内存
这个就是GC所做的事情了,参考c#GC。
非托管资源
应用使用完非托管资源后,必须要显式释放这些资源。最常见的非托管资源类型是包装操作系统资源的对象。虽然垃圾回收器可以跟踪封装非托管资源的对象的生存期,但是无法了解如何发布并清理这些非托管资源。所以清理需要做以下操作:
- 实现清理模式:需要提供IDisposable.Dispose实现以启用非托管资源的确定性释放。当不再需要此对象时,类型使用者可以调用Dispose。Dispose方法立即释放非托管资源。
-
在类型使用者忘记调用Dispose的情况下,准备释放非托管资源。有两种方法可实现此目的:
- 使用安全句柄包装非托管资源。安全句柄派生自System.Runtime.InteropServices.SafeHandle类并包含可靠的Finalize方法。在使用安全句柄时,只需要实现IDisposable接口并在Dispose实现中调用安全句柄的IDisposable.Dispose方法。如果未调用安全句柄Dispose方法,则垃圾回收器将自动调用安全句柄的终结器。
或 - 重写Object.Finalize方法。当类型使用者无法调用IDisposable.Dispose以确定性地释放非托管资源时,终止会启动对非托管资源的非确定性释放。
- 使用安全句柄包装非托管资源。安全句柄派生自System.Runtime.InteropServices.SafeHandle类并包含可靠的Finalize方法。在使用安全句柄时,只需要实现IDisposable接口并在Dispose实现中调用安全句柄的IDisposable.Dispose方法。如果未调用安全句柄Dispose方法,则垃圾回收器将自动调用安全句柄的终结器。
然后,类型使用者可直接调用IDisposable.Dispose实现以释放非托管资源使用的内存。在正确实现Dispose方法时,安全句柄的Finalize方法或Object.Finalize方法的重写会在未调用Dispose方法的情况下阻止清理资源。