积极答复者
托管代码?

问题
答案
-
我来解释一下什么是 Handle 以及 SafeHandle。
Handle 大家都知道,是一个 32 位 (x64 为 64 位) 的整形指针,在 C++ 描述为 HWND。它用来唯一标识 Windows 中的系统资源,比如文件、Mutex、原子、进程、库、控件、注册表等等。
但在托管代码中,引用到系统资源后,必须显式去 Dispose 这些资源,否则,就会造成系统资源没有释放,以至于内存泄漏。.NET 提供一套机制,IDisposable 设计模式,来解决这个问题。在一个引用了非托管 Windows 资源的类型中,必须实现 System.IDisposable 来正确释放其资源。
下面是一个示例实现。
public class BinaryFileWriter : IDisposable
{
private MemoryStream _stream;
private string _filePath;
private byte[] _data;
public BinaryFileWriter(string filePath, byte[] data)
{
if (filePath == null) throw new ArgumentNullException("filePath");
if (data == null) throw new ArgumentNullException("data");
this._stream = new MemoryStream(data);
this._filePath = filePath;
this._data = data;
}
public void Save()
{
using (StreamWriter writer = new StreamWriter(File.Open(this._filePath))
{
writer.WriteBytes(this._data);
}
}
public void Dispose()
{
this.Dispose(true);
// 这一句话告诉 GC 此类型已经实现 IDisposable,不要调用其析构函数。
System.GC.SupressFinalizer(this);
}
public void Dispose(bool disposing)
{
if (disposing)
{
// 释放非托管资源。
this._stream.Dispose();
}
}
public ~BinaryFileWriter()
{
this.Dispose(false);
}
}可以看到,IDisposable 设计模式中必须有 Dispose() 以及 Dispose(bool) 两个重载决策。并且,由于 Dispose 方法代替析构函数清理非托管资源,如果它抛出异常,则整个 GC 机制将会受到影响而导致无法预期的结果。因此,Dispose 并不期待产生异常。同样,析构函数也不期待任何异常。
然而,有些情况下,在设计类型时,不能在代码层次保证所有的资源清理都不产生异常。如果一旦产生异常,特别是在析构函数调用时异常,该类型实例即无法正确清理资源,导致 GC 回收失败,从而导致内存泄漏。为了解决这一问题,Microsoft 引入了几个非常特殊的类型,他们是:
CriticalFinalizerObject
SafeHandle
ZeroOrMinusOneIsInvalid
...
SafeFileHandle
SafeLibraryHandle
...CriticalFinalizerObject 能够保证所有继承这个类型的类型实例都能正确被回收。SafeHandle 继承于该类型,用来表示一个安全句柄。在使用它的时候,CLR 保证这些句柄能够被正确清理以及回收。所以,您的代码分析工具要求您使用 SafeHandle 以便代替 HandleRef 或者 IntPtr。
CLR 对于 CriticalFinalizerObject 有其特殊的处理方式,就像 MarshalByRefObject 一样。
如果需要了解更多详情,请联系我。
Mark Zhou- 已标记为答案 王基任 2010年9月26日 15:16
全部回复
-
我来解释一下什么是 Handle 以及 SafeHandle。
Handle 大家都知道,是一个 32 位 (x64 为 64 位) 的整形指针,在 C++ 描述为 HWND。它用来唯一标识 Windows 中的系统资源,比如文件、Mutex、原子、进程、库、控件、注册表等等。
但在托管代码中,引用到系统资源后,必须显式去 Dispose 这些资源,否则,就会造成系统资源没有释放,以至于内存泄漏。.NET 提供一套机制,IDisposable 设计模式,来解决这个问题。在一个引用了非托管 Windows 资源的类型中,必须实现 System.IDisposable 来正确释放其资源。
下面是一个示例实现。
public class BinaryFileWriter : IDisposable
{
private MemoryStream _stream;
private string _filePath;
private byte[] _data;
public BinaryFileWriter(string filePath, byte[] data)
{
if (filePath == null) throw new ArgumentNullException("filePath");
if (data == null) throw new ArgumentNullException("data");
this._stream = new MemoryStream(data);
this._filePath = filePath;
this._data = data;
}
public void Save()
{
using (StreamWriter writer = new StreamWriter(File.Open(this._filePath))
{
writer.WriteBytes(this._data);
}
}
public void Dispose()
{
this.Dispose(true);
// 这一句话告诉 GC 此类型已经实现 IDisposable,不要调用其析构函数。
System.GC.SupressFinalizer(this);
}
public void Dispose(bool disposing)
{
if (disposing)
{
// 释放非托管资源。
this._stream.Dispose();
}
}
public ~BinaryFileWriter()
{
this.Dispose(false);
}
}可以看到,IDisposable 设计模式中必须有 Dispose() 以及 Dispose(bool) 两个重载决策。并且,由于 Dispose 方法代替析构函数清理非托管资源,如果它抛出异常,则整个 GC 机制将会受到影响而导致无法预期的结果。因此,Dispose 并不期待产生异常。同样,析构函数也不期待任何异常。
然而,有些情况下,在设计类型时,不能在代码层次保证所有的资源清理都不产生异常。如果一旦产生异常,特别是在析构函数调用时异常,该类型实例即无法正确清理资源,导致 GC 回收失败,从而导致内存泄漏。为了解决这一问题,Microsoft 引入了几个非常特殊的类型,他们是:
CriticalFinalizerObject
SafeHandle
ZeroOrMinusOneIsInvalid
...
SafeFileHandle
SafeLibraryHandle
...CriticalFinalizerObject 能够保证所有继承这个类型的类型实例都能正确被回收。SafeHandle 继承于该类型,用来表示一个安全句柄。在使用它的时候,CLR 保证这些句柄能够被正确清理以及回收。所以,您的代码分析工具要求您使用 SafeHandle 以便代替 HandleRef 或者 IntPtr。
CLR 对于 CriticalFinalizerObject 有其特殊的处理方式,就像 MarshalByRefObject 一样。
如果需要了解更多详情,请联系我。
Mark Zhou- 已标记为答案 王基任 2010年9月26日 15:16
-
我来解释一下什么是 Handle 以及 SafeHandle。
Handle 大家都知道,是一个 32 位 (x64 为 64 位) 的整形指针,在 C++ 描述为 HWND。它用来唯一标识 Windows 中的系统资源,比如文件、Mutex、原子、进程、库、控件、注册表等等。
但在托管代码中,引用到系统资源后,必须显式去 Dispose 这些资源,否则,就会造成系统资源没有释放,以至于内存泄漏。.NET 提供一套机制,IDisposable 设计模式,来解决这个问题。在一个引用了非托管 Windows 资源的类型中,必须实现 System.IDisposable 来正确释放其资源。
下面是一个示例实现。
public class BinaryFileWriter : IDisposable
{
private MemoryStream _stream;
private string _filePath;
private byte[] _data;
public BinaryFileWriter(string filePath, byte[] data)
{
if (filePath == null) throw new ArgumentNullException("filePath");
if (data == null) throw new ArgumentNullException("data");
this._stream = new MemoryStream(data);
this._filePath = filePath;
this._data = data;
}
public void Save()
{
using (StreamWriter writer = new StreamWriter(File.Open(this._filePath))
{
writer.WriteBytes(this._data);
}
}
public void Dispose()
{
this.Dispose(true);
// 这一句话告诉 GC 此类型已经实现 IDisposable,不要调用其析构函数。
System.GC.SupressFinalizer(this);
}
public void Dispose(bool disposing)
{
if (disposing)
{
// 释放非托管资源。
this._stream.Dispose();
}
}
public ~BinaryFileWriter()
{
this.Dispose(false);
}
}可以看到,IDisposable 设计模式中必须有 Dispose() 以及 Dispose(bool) 两个重载决策。并且,由于 Dispose 方法代替析构函数清理非托管资源,如果它抛出异常,则整个 GC 机制将会受到影响而导致无法预期的结果。因此,Dispose 并不期待产生异常。同样,析构函数也不期待任何异常。
然而,有些情况下,在设计类型时,不能在代码层次保证所有的资源清理都不产生异常。如果一旦产生异常,特别是在析构函数调用时异常,该类型实例即无法正确清理资源,导致 GC 回收失败,从而导致内存泄漏。为了解决这一问题,Microsoft 引入了几个非常特殊的类型,他们是:
CriticalFinalizerObject
SafeHandle
ZeroOrMinusOneIsInvalid
...
SafeFileHandle
SafeLibraryHandle
...CriticalFinalizerObject 能够保证所有继承这个类型的类型实例都能正确被回收。SafeHandle 继承于该类型,用来表示一个安全句柄。在使用它的时候,CLR 保证这些句柄能够被正确清理以及回收。所以,您的代码分析工具要求您使用 SafeHandle 以便代替 HandleRef 或者 IntPtr。
CLR 对于 CriticalFinalizerObject 有其特殊的处理方式,就像 MarshalByRefObject 一样。
如果需要了解更多详情,请联系我。
Mark Zhou