闲话资源管理

对于 .NET 开发人员来说程序的资源管理是必不可少的,要开发出一款优秀的应用就必须明白 .NET 的资源管理机制。在 .NET 中垃圾回收器 GC 帮助我们管理托管资源,在开发时我们不需要过多的关注大多数内存问题(例如:内存泄漏、dangling pointer)。

零、托管资源

在 .NET 中一个经典的资源管理的例子就是 GC 对循环引用的管理。 GC 的 Mark and Compact 算法会快速的检测出由简单或复杂的关系网所形成的循环引用,并把所有不可达的对象是为一个整体从内存中清理出去。 GC 会检测出和应用程序根对象没有任何通路相连的对象,然后判定这些对象为不可达对象,接着将这些对象从内存中清理出去,最后 GC 将会压缩托管堆把其中活动的对象放在一起,把空闲的内存放在一起形成连续的内存区域。简单地说就是当应用程序不再使用(引用)某个对象时 GC 就会认定这个对象是可回收的。

一、非托管资源

前面我们一直在说托管资源,托管资源由 GC 负责因此我们不需要干涉太多,我们需要重点干涉的就是非托管资源。非托管资源包括数据库连接、 COM 对象、 GDI+ 对象以及其他系统对象,这种资源我们如果不加以干涉的话会造成内存大量占用,进而程序崩溃操作系统崩溃。
在 .NET 中针对非托管资源的控制一共有两种方法:一种是 finalizer 另一种是 IDisposable 。前者的机制存在很多缺陷,因此我们在开发中一般会使用 IDisposable 来释放非托管资源。下面我们来详细的讲解一下为何要选择后者而不是前者。
一般情况下如果对象存在 finalizer 时 GC 会在判定该对象为垃圾后择机调用 finalizer 。但是 finalizer 并不是及时执行的,也就是说程序在退出临界区域时,相关的对象(这里的对象指的时方法、类、窗体等)并不是一执行完就立马退出,而是会在内存中停留一段时间。因此 finalizer 只能保证非托管资源最终可以被释放,但是不保证在何时释放。并且大量依赖 finalizer 会降低程序的性能,这是因为垃圾回收器需要执行更多的工作才能最终终结这些资源。 GC 清理带有 finalizer 的垃圾对象是这么做的:首先 GC 会在每个周期内把包含 finalizer 但还没有执行的对象放在队列中,在下一个周期里 GC 会清理掉已经执行过 finalizer 的对象,这其中执行对象 finalizer 的线程并不是 GC 所在的线程。
在这里需要注意的是具有 finalizer 的对象并不是只存在与一个周期中,而是有可能存在于多个周期中。.NET 为优化垃圾回收工作,特意定义了 generation (世代) 这个概念,.NET 中一共包含 3个世代,第一次垃圾回收之后创建出来的对象叫做 0 代对象,再一次执行垃圾回收后依然存在的对象叫做 1 代对象,经过两次或多次依然存在的对象则叫做 2 代对象。然后世代的存在给具有 finalizer 的对象带来了我发及时释放的问题,因为 GC 在每次执行周期都会判断 0 代对象是不是垃圾,每执行 10 个周期就会判断一次 1 代对象是不是垃圾,到了 2 代对象这里则会执行 100 个周期才会判断一次,因此具有 finalizer 的对象最少需要 GC 执行 10 个周期才会清理,而不具有 finalizer 的对象最多 9 个周期就会被清理。

二、小结

这一篇文章就这么短,通过这篇文章我们需要注意在需要使用非托管资源的时候优先选择 IDisposable 接口,除非是必须使用 finalizer 。后面的文章我将详细讲解怎么样的编码才能算是具有良好的资源管理的编码。

发布了215 篇原创文章 · 获赞 103 · 访问量 36万+

猜你喜欢

转载自blog.csdn.net/gangzhucoll/article/details/105401374