8

我有以下代码块:

IntPtr unmanagedPointer = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, unmanagedPointer, buffer.Length);
SomeCommandThatCanThrowAnException();
Marshal.FreeHGlobal(unmanagedPointer);

是否应该将该块包装在 try 中,并将 FreeHGlobal 命令放在 finally 块中。(以防中间命令抛出异常)。

在这种情况下,finally 可以防止内存泄漏似乎是有道理的,但是从我在网上找到的示例来看,finally 没有被使用。也许无论如何这些资源都会被自动处理掉(即使它们是不受管理的)。

4

2 回答 2

15

使用 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 作为基类。

于 2010-08-19T20:38:55.657 回答
2

绝对地。它永远不会自动释放,它是非托管内存。

于 2010-08-19T20:39:03.340 回答