使用 Marshal.AllocHGlobal 分配的非托管内存不会自动释放。
所以将 Marshal.FreeHGlobal 放在一个finally
块中确实是一个好主意:
IntPtr unmanagedPointer = Marshal.AllocHGlobal(buffer.Length);
try
{
Marshal.Copy(buffer, 0, unmanagedPointer, buffer.Length);
SomeCommandThatCanThrowAnException();
}
finally
{
Marshal.FreeHGlobal(unmanagedPointer);
}
为简洁起见,您发现的示例可能省略了错误处理。
如果您为长期目的分配非托管内存(即不在同一个方法中释放它),您可能有兴趣将指针包装在派生自SafeHandle的对象中(例如SafeBuffer)。
SafeHandle实现了 IDisposable 模式,因此当您释放对象或垃圾收集器收集对象时,将释放非托管内存。SafeHandle 还派生自 CriticalFinalizerObject 类,这意味着它将从 CLR 获得特殊处理,以确保真正释放内存。
class HGlobal : SafeBuffer
{
public HGlobal(int cb)
: base(true)
{
this.SetHandle(Marshal.AllocHGlobal(cb));
this.Initialize((ulong)cb);
}
protected override bool ReleaseHandle()
{
Marshal.FreeHGlobal(this.handle);
return true;
}
}
例子:
using (var h = new HGlobal(buffer.Length))
{
h.WriteArray(0, buffer, 0, buffer.Length);
}
注意:SafeBuffer 是一个野兽,所以建议小心。
注意 2:SafeHandles 可以很好地与 P/Invoke 配合使用,并且完全不需要传递 IntPtrs。
SafeBuffers 用于从 C# 安全地操作非托管内存,因此根据您正在执行的操作(分配非托管内存以用于 P/Invoke,或从 C# 操作非托管内存),您应该适当地选择 SafeHandle 或 SafeBuffer 作为基类。