.net中非托管资源如何清理
2009-02-24 16:00
246 查看
本篇文章是我对.net中非托管资源清理的一些见解,共享给大家,希望能够给初学者提供一些借鉴的地方.
.net中非托管资源如何清理
背景
这两天帮助其它项目组Review代码,发现有些地方实现了IDispose接口,同时也发现了一些关于IDispose的问题:
1. A类型实现了IDispose接口,B类型里面含有A类型的字段,B类型没有实现IDispose接口
2. 一个类里面实现了Finalize终结器,同时也实现了IDispose接口,但在Dispose方法里面没有调用GC.SuppressFinalize(this)方法.
下面我对以上两个问题分别分析一下,并提出解决方案.
问题1:如果A类型里面有非托管资源需要在实现的IDispose接口里面释放,由于B类型没有实现IDispose接口,B类型的使用者要想释放A类型的非托管资源并不方便.这样的话,就有可能忘记了释放A类型的非托管资源.
解决方案:
实现B类型的IDispose接口,在Dispose方法里面调用A类型的Dispose方法.这样,B类型的使用者在调用B类型Dispose的同时,就把A类型的Dispose也调用了.
问题2:在Dispose方法里面没有调用GC.SuppressFinalize(this)方法,会有什么问题呢,这样会导致垃圾回收器不能对这个类型的对象及时回收. 当GC开始工作的时候,它首先将没有终结器的垃圾对象从内存中移除,有终结器的所有对象则添加到一个垃圾队列当中。GC会调用一个新线程来执行这些对象的终结器。当终结器执行完毕后,这个对象会从队列中被移除。这个对象在队列中移除之后,当GC再次开始工作的时候,这个对象才能够被回收,所以有终结器的对象会比没有的在内存中保留更长的时间。在后面我会对这里再详细的描述一下.
解决方案:
在Dispose方法中调用GC.SuppressFinalize(this)方法.这样的话,就不会把有终结器的对象则添加到垃圾队列当中.
切入正题
.net中,非托管代码清理有两种方式:Finalize方式和Dispose方式.
Finalize方式:通过对自定义类型实现一个Finalize方法来释放非通过资源.
从.net2.0开始,C#编译器不能对Finalize进行显示的调用和重写,必须使用析构函数来实现它.
public class MyResourceRelease: IDisposable
{
~MyResourceRelease()
{
Dispose(false);
}
/// <summary>
/// 保证资源只用释放一次
/// </summary>
private bool _alreadyDisposed = false;
/// <summary>
///
/// </summary>
/// <param name="isDisposing">用来判断释放资源的类别(托管和非托管)</param>
protected virtual void Dispose(bool isDisposing)
{
if(_alreadyDisposed)
{
return;
}
if(isDisposing)
{
//释放托管资源
}
//释放非托管资源
_alreadyDisposed = true;
}
public void Dispose()
{
Dispose(true);
//阻止GC把该对象放入终结器队列
GC.SuppressFinalize(this);
}
.net中非托管资源如何清理
背景
这两天帮助其它项目组Review代码,发现有些地方实现了IDispose接口,同时也发现了一些关于IDispose的问题:
1. A类型实现了IDispose接口,B类型里面含有A类型的字段,B类型没有实现IDispose接口
2. 一个类里面实现了Finalize终结器,同时也实现了IDispose接口,但在Dispose方法里面没有调用GC.SuppressFinalize(this)方法.
下面我对以上两个问题分别分析一下,并提出解决方案.
问题1:如果A类型里面有非托管资源需要在实现的IDispose接口里面释放,由于B类型没有实现IDispose接口,B类型的使用者要想释放A类型的非托管资源并不方便.这样的话,就有可能忘记了释放A类型的非托管资源.
解决方案:
实现B类型的IDispose接口,在Dispose方法里面调用A类型的Dispose方法.这样,B类型的使用者在调用B类型Dispose的同时,就把A类型的Dispose也调用了.
问题2:在Dispose方法里面没有调用GC.SuppressFinalize(this)方法,会有什么问题呢,这样会导致垃圾回收器不能对这个类型的对象及时回收. 当GC开始工作的时候,它首先将没有终结器的垃圾对象从内存中移除,有终结器的所有对象则添加到一个垃圾队列当中。GC会调用一个新线程来执行这些对象的终结器。当终结器执行完毕后,这个对象会从队列中被移除。这个对象在队列中移除之后,当GC再次开始工作的时候,这个对象才能够被回收,所以有终结器的对象会比没有的在内存中保留更长的时间。在后面我会对这里再详细的描述一下.
解决方案:
在Dispose方法中调用GC.SuppressFinalize(this)方法.这样的话,就不会把有终结器的对象则添加到垃圾队列当中.
切入正题
.net中,非托管代码清理有两种方式:Finalize方式和Dispose方式.
Finalize方式:通过对自定义类型实现一个Finalize方法来释放非通过资源.
从.net2.0开始,C#编译器不能对Finalize进行显示的调用和重写,必须使用析构函数来实现它.
public class MyResourceRelease: IDisposable
{
~MyResourceRelease()
{
Dispose(false);
}
/// <summary>
/// 保证资源只用释放一次
/// </summary>
private bool _alreadyDisposed = false;
/// <summary>
///
/// </summary>
/// <param name="isDisposing">用来判断释放资源的类别(托管和非托管)</param>
protected virtual void Dispose(bool isDisposing)
{
if(_alreadyDisposed)
{
return;
}
if(isDisposing)
{
//释放托管资源
}
//释放非托管资源
_alreadyDisposed = true;
}
public void Dispose()
{
Dispose(true);
//阻止GC把该对象放入终结器队列
GC.SuppressFinalize(this);
}
相关文章推荐
- 一起谈.NET技术,如何让ASP.NET默认的资源编程方式支持非.ResX资源存储
- C#温故而知新学习系列之.NET运行机制—.NET中非托管代码是指什么?(二)
- 如何快速清理 docker 资源的方法
- android studio 如何清理没有用到的资源文件
- 如何在.Net 中使用资源文件?
- 是否可以完全相信垃圾回收?.Net 托管资源 非托管资源 垃圾回收 的疑问。
- 构建高性能ASP.NET站点 第七章 如何解决内存的问题(前中篇)―托管资源优化―监测CLR性能
- .net中的托管资源与非托管资源
- 《Effective C#》快速笔记(二)- .NET 资源托管
- .NET.GC 浅谈.net托管程序中的资源释放问题
- 是否可以完全相信垃圾回收?.Net 托管资源 非托管资源 垃圾回收 的疑问。
- 构建高性能ASP.NET站点 第七章 如何解决内存的问题(前篇)―托管资源优化―垃圾回收机制深度剖析
- [.NET] 《Effective C#》快速笔记(二)- .NET 资源托管
- 构建高性能ASP.NET站点 第七章 如何解决内存的问题(后篇)―托管资源优化―监常用优化措施
- "资源托管"及"非托管资源"问题 --- 创建MDB文件时如何释放LDB为例
- [转].NET.GC 浅谈.net托管程序中的资源释放问题
- .NET.GC 浅谈.net托管程序中的资源释放问题 (转帖)
- .NET.GC 浅谈.net托管程序中的资源释放问题
- 【原创】构建高性能ASP.NET站点 第七章 如何解决内存的问题(前篇)—托管资源优化—垃圾回收机制深度剖析