是否应该将 Marshal.FreeHGlobal 放置在finally 块中以确保资源得到释放?

发布于 2024-09-15 12:50:04 字数 406 浏览 10 评论 0 原文

我有以下代码块:

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

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

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

I have the following block of code:

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

Should the block be wrapped in a try, and the FreeHGlobal command be placed in a finally block. (In case the middle command throws an exception).

It seems to make sense that finally would prevent memory leaks in this case, however from examples that I have found online, finally is not used. Perhaps the resources get automatically disposed of anyway (even though they are unmanaged).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

桃扇骨 2024-09-22 12:50:04

使用 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 配合良好,并且完全不需要传递 IntPtr。

SafeBuffers 用于安全地从 C# 操作非托管内存,因此根据您正在执行的操作(分配非托管内存以与 P/Invoke 一起使用,或从 C# 操作非托管内存),您应该适当选择 SafeHandle 或 SafeBuffer 作为基类。

Unmanaged memory allocated with Marshal.AllocHGlobal is not automatically released.

So putting Marshal.FreeHGlobal in a finally block is indeed a good idea:

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

The examples you've found probably omit error handling for brevity.


If you're allocating unmanaged memory for long-term purposes (i.e. not freeing it within the same method), you might be interested in wrapping the pointer in an object that derives from SafeHandle (such as SafeBuffer).

SafeHandle implements the IDisposable pattern, so the unmanaged memory will be freed when you dispose the object or when the garbage collector collects the object. SafeHandle also derives from the CriticalFinalizerObject class which means it will get special treatment from the CLR to make sure the memory is really freed.

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;
    }
}

Example:

using (var h = new HGlobal(buffer.Length))
{
    h.WriteArray(0, buffer, 0, buffer.Length);
}

Note: SafeBuffer is quite a beast, so caution is advised.

Note 2: SafeHandles work well with P/Invoke and eliminate the need to pass around IntPtrs entirely.

SafeBuffers are for safely manipulating unmanaged memory from C#, so depending on what you're doing (allocating unmanaged memory for use with P/Invoke, or manipulating unmanaged memory from C#) you should choose SafeHandle or SafeBuffer as base class appropriately.

过度放纵 2024-09-22 12:50:04

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

Absolutely. It never gets released automatically, it is unmanaged memory.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文