【安全编程向】【.NET】【C#】持有非托管资源的实现了 IDisposable 接口的对象的使用后处理

随笔3个月前发布 雁妮读书
32 0 0

什么是非托管资源?

非托管资源是指那些不由 .NET 垃圾回收器(GC)管理的资源。它们通常由操作系统或外部库管理,需要显式地释放。这些资源包括但不限于:

文件句柄
数据库连接
网络连接
计时器
图形资源(如位图、画笔、设备上下文)
内存指针

什么样的类会持有非托管资源?

持有非托管资源的类通常需要与操作系统或外部库进行交互,以执行一些底层操作或资源管理。这些类包括但不限于:

文件和流相关的类

FileStream: 读取和写入文件流。
StreamReaderStreamWriter: 用于读取和写入文本文件。
MemoryStream: 在内存中处理流数据。
BufferedStream: 为另一个流添加一个缓冲层。
BinaryReaderBinaryWriter: 用于读取和写入二进制数据。

数据库相关的类

SqlConnection: 代表与 SQL Server 数据库的连接。
SqlCommand: 用于在数据库上执行命令。
SqlDataReader: 用于从数据库中读取数据流。

图形和资源管理类

Bitmap: 用于处理图像数据。
Graphics: 提供绘图方法。
PenBrush: 用于绘图操作。

计时器类

Timer: 提供基于时间的事件。
System.Threading.Timer: 提供线程池计时器功能。

网络相关的类

TcpClientTcpListener: 用于 TCP 网络通信。
UdpClient: 用于 UDP 网络通信。
HttpClient: 用于发送 HTTP 请求并从 HTTP 服务器接收响应。

加密和安全相关的类

RijndaelManaged: 提供加密和解密功能。
SHA256Managed: 用于计算 SHA-256 哈希值。

界面相关的类

Form: Windows 窗体应用程序中的表单。
Control: Windows 窗体应用程序中的控件基类。

其他常见的类

CancellationTokenSource: 提供取消异步操作的功能。
Mutex: 提供跨进程同步功能。
SemaphoreSemaphoreSlim: 提供线程同步功能。

 

在使用持有非托管资源的类后需要处理

垃圾回收器(GC)负责回收托管堆中的内存,即由 .NET 运行时管理的对象内存。然而,非托管资源(如文件句柄、数据库连接、网络连接等)是由操作系统或外部库管理的资源,不在托管堆中。因此,GC 无法自动释放这些资源。

为了解决这个问题,.NET 提供了 IDisposable 接口,允许开发者定义显式的资源释放逻辑,以确保在对象不再使用时正确地释放非托管资源。这种机制确保了资源的有效管理和系统性能的优化。

IDisposable 接口定义了 Dispose 方法。实现 IDisposable 接口的类必须提供 Dispose 方法的实现。

是因为持有非托管资源的对象所持有的资源类型各不相同,.NET 提供了 IDisposable 接口,允许开发者定义显式的资源释放逻辑。这样,开发者可以根据具体的非托管理资源和应用场景,灵活地实现资源释放的方法。

也即,不同的类的Dispose()方法也不同。

 

在实例化持有非托管资源的类时使用using语句

using 语句是为方便管理实现了 IDisposable 接口的对象而设计的,特别是那些持有非托管资源的对象。它可以确保这些对象在使用完毕后自动调用其 Dispose 方法,从而释放资源。

using 语句的工作原理

using 语句在语法上简化了资源的管理,使得代码更加简洁且易于阅读。其内部工作原理是通过编译器的转换来实现的。以下是一个示例以及其编译后的等价代码:

示例代码:
using (var resource = new MyResource())
{
    // 使用资源
}

编译后的等价代码:

                var resource = new MyResource();
                try
                {
                    // 使用资源
                }
                finally
                {
                    if (resource != null)
                    {
                        ((IDisposable)resource).Dispose();
                        //语句中的前置括号和类型转换用于确保调用的是实现自 IDisposable 接口的 Dispose 方法。这种方式确保编译器知道应该调用哪个 Dispose 方法。
                        //如果对象被声明为一个不包含 Dispose 方法的类型(例如object 或其他基类基类或接口),编译器不会知道该对象可以调用 Dispose 方法。类型转换确保编译器将对象视为实现了 IDisposable 接口的类型,以便可以调用其 Dispose 方法。
                        //类型转换可以确保调用特定接口的实现方法,防止调用被隐藏或重载的其他方法。
                    }
                }

 

如果不处理持有非托管资源的类,程序可能引发的问题

如果持有非托管资源的类没有正确释放其资源,而在程序中不断创建新的实例,可能会引发以下问题:

资源泄漏

非托管资源不会被垃圾回收器自动回收,如果不显式释放,这些资源会一直被占用。这会导致:

文件句柄泄漏:打开的文件句柄不会关闭,可能导致文件无法被其他程序访问。
内存泄漏:内存中的资源(如位图、设备上下文)不会被释放,导致内存占用不断增加。
连接泄漏:数据库连接、网络连接等不会关闭,可能导致服务器资源耗尽,影响系统稳定性。

性能下降

随着未释放的资源数量增加,系统性能会逐渐下降。这包括:

内存使用率增加:未释放的资源占用内存,导致系统可用内存减少。
系统响应变慢:由于资源耗尽,系统响应时间增加,操作变得缓慢。
应用程序崩溃:如果资源耗尽达到一定程度,应用程序可能无法继续运行,导致崩溃。

系统稳定性问题

长时间运行的应用程序特别容易受到资源泄漏的影响。如果不正确释放资源,系统稳定性会受到严重影响,包括但不限于:

系统资源耗尽:文件句柄、内存、网络连接等资源耗尽,导致系统无法正常工作。
服务不可用:关键服务因资源耗尽而无法正常运行,导致服务中断。

 

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...