(c#) 销毁资源和释放内存

本文详细介绍了.NET框架中资源的概念,包括托管资源与非托管资源的区别,以及如何正确地进行资源释放。同时深入探讨了垃圾回收机制的工作原理,帮助开发者更好地理解如何优化内存管理和提升程序性能。

0. 什么是资源? .NET 框架中如何访问资源?

所谓的资源就是程序中可利用的数据,譬如:字符串、图片和任何二进制数据,包括任何类型的文件。

在面向对象的环境中,每一个类型都标识为某些程序所用的资源,要想使用这些资源,必须为相应的类型分配一定的内存空间。

访问一个资源需要如下几个步骤:

1)分配内存空间: 调用中间语言(IL)中的newobj指令(使用new操作符时,将产生newobj指令),为某个特定资源的类型分配一定的内存空间。
2) 初始化内存: 一个类型的实例构造器负责这样的初始化工作。
3)使用资源: 通过访问类型成员来使用资源。根据需要会有反复。
4)销毁资源: 执行清理工作。
5)释放内存: 托管堆上的内存由GC全权负责, 值引用的在栈上的内存会随着栈空间的消亡而自动消失。

1. 什么是托管资源,非托管资源?

托管资源是由CLR全权负责的资源,CLR不负责的资源位非托管资源。

对于托管资源通过GC自动回收。

对于非托管资源GC管理,通过代码调用手动进行清除。

2. 什么是垃圾, 什么是垃圾回收?

Net类型分为两大类,一个就是值类型,另一个就是引用类型。前者是分配在栈上,并不需要GC回收;后者是分配在堆上,因此它的内存释放和回收需要通过GC来完成,

那么只有被称为垃圾的对象才能被GC回收。也就是说,一个引用类型对象所占用的内存需要被GC回收,需要先成为垃圾。

那么.Net如何判定一个引用类型对象是垃圾呢,.Net的判断很简单,只要判定此对象或者其包含的子对象没有任何引用是有效的,那么系统就认为它是垃圾。

内存的释放和回收需要伴随着程序的运行,因此系统为GC安排了独立的线程。那么GC的工作大致是,查询内存中对象是否成为垃圾,然后对垃圾进行释放和回收。

那么对于GC对于内存回收采取了一定的优先算法进行轮循回收内存资源。

其次,对于内存中的垃圾分为两种,一种是需要调用对象的析构函数,另一种是不需要调用的

GC对于前者的回收需要通过两步完成,第一步是调用对象的析构函数,第二步是回收内存,但是要注意这两步不是在GC一次轮循完成,即需要两次轮循;相对于后者,则只是回收内存。

3. 如何正确的释放资源?

托管的内存资源,这是不需要我们操心的,系统已经为我们进行管理了。

对于非托管的资源,这里再重申一下,就是Stream,数据库的连接,GDI+的相关对象,还有Com对象等等这些操作系统资源,需要我们手动去释放。

解决方案:

  1. 基于架构师的经验,或者统一意见会议,在开发前对开发人员进行必要的统一培训,对关系到性能、内存等会影响系统稳定性的解决方案进行培训。这本身既有助于开发人员水平的提高,也对系统的稳定性方面提供很大的保障(项目可不是做完一个就没了,这种培训会带来长久的增益效果)。

  2. 注意托管资源与非托管资源的释放区别,非托管资源是需要手动释放的。

  3. 使用using关键字,避免忘记Dispose的情况,如上面的ShowDialog问题。(using中还起到了try-catch的作用,避免由于异常未调用Dispose的情况)

  4. 使用UnLoad事件或者析构函数,对注册的全局事件进行取消注册。

  5. 特别注意自定义组件的稳定性更重要,发生问题时影响也更广。注意继承IDisposable接口,进行资源释放,并且对有疑问/复杂逻辑的地方添加try-catch语句。(发现问题的人员不一定有权限修改这些组件)一定要保证组件健壮健壮,再健壮。这是我做组件的感悟~~~多么痛的领悟!!

如何去释放,应该把这些操作放到哪里比较好呢。.Net提供了三种方法,也是最常见的三种,大致如下:

1. 析构函数;

2. 继承IDisposable接口,实现Dispose方法;

3. 提供Close方法。

析构函数Dispose方法Close方法
意义销毁对象销毁对象
调用方式不能被显示调用,会被GC调用需要显示调用或者通过using语句
调用时机不确定确定,在显示调用或者离开using程序块

http://www.cnblogs.com/fdyang/p/3456258.html

### ### C# 中如何正确释放内存资源C# 中,内存管理主要由 .NET 的垃圾回收器(Garbage Collector,GC)负责,自动回收不再使用的对象所占用的内存。然而,在某些特定场景下,仍然需要手动干预资源释放,尤其是在处理非托管资源(如文件句柄、网络连接、图像资源等)时。 #### 显式释放资源 对于实现了 `IDisposable` 接口的对象,应调用其 `Dispose()` 方法以释放资源。这种方式适用于数据库连接、文件流、图像对象等非托管资源。例如: ```csharp using (FileStream fs = new FileStream("file.txt", FileMode.Open)) { // 使用文件流 } // 离开 using 块后,fs 会自动调用 Dispose() ``` `using` 语句确保对象在使用完毕后立即释放资源,是推荐的做法。若不使用 `using`,则需手动调用 `Dispose()`,并确保在不再需要资源时调用。 #### 垃圾回收机制 尽管 `Dispose()` 可以显式释放资源,但垃圾回收器仍负责回收托管内存。在某些情况下,可以调用 `GC.Collect()` 强制进行垃圾回收: ```csharp GC.Collect(); GC.WaitForPendingFinalizers(); ``` 但应谨慎使用此方法,因为频繁调用会影响性能,通常应依赖 GC 的自动管理。 #### 非托管资源释放 对于包含非托管资源对象(如通过 P/Invoke 调用 Win32 API 获取的资源),应实现 `IDisposable` 接口,并在 `Dispose()` 方法中释放这些资源。例如: ```csharp public class NativeResourceWrapper : IDisposable { private IntPtr nativeHandle; public NativeResourceWrapper(IntPtr handle) { nativeHandle = handle; } public void Dispose() { ReleaseNativeResource(nativeHandle); nativeHandle = IntPtr.Zero; } [DllImport("NativeLibrary.dll")] private static extern void ReleaseNativeResource(IntPtr handle); } ``` 此方法确保在对象销毁时,非托管资源也能被正确释放。 #### 事件订阅的清理 在订阅系统级事件(如 `SystemEvents.UserPreferenceChanged`)时,若不再需要响应事件,应取消订阅以防止内存泄漏: ```csharp SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged; ``` 此操作应放在 `Dispose()` 方法中或对象生命周期结束时执行,以避免事件持续持有对象引用。 #### 总结 在 C#释放内存资源时,应优先依赖垃圾回收机制,同时对实现了 `IDisposable` 的对象进行显式释放。对于非托管资源,应确保在 `Dispose()` 中正确释放,并在必要时取消事件订阅,以避免内存泄漏[^2]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值