【问题标题】:Does C# clean up C++ allocated memory?C# 是否清理 C++ 分配的内存?
【发布时间】:2010-10-15 17:11:11
【问题描述】:

我有一个具有以下签名的假设 COM 对象

void MemAlloc(ref double[] test, int membercount)

在 C++ 中使用 new/malloc 分配内存的位置。一旦这是在 C# 中,使用 RCW,我如何确保正确释放内存?我认为 .NET 很难释放,考虑到在 C++ 中,您需要知道它是否分配有 new/malloc/mm_malloc,然后才能正确释放它。那么,清理我的 C++ 分配数组的合适方法是什么?谢谢。

【问题讨论】:

    标签: c# c++ com interop


    【解决方案1】:

    我相信您应该使用 CoTaskMemAlloc() 来获取要从托管端显式释放的内存。一旦内存不再可访问,CLR 将负责释放内存。如果你想明确地释放它,你可以使用托管 Marshal.CoTaskFree() 例程。

    一般来说,interop marshaler 和 CLR 遵守 COM 释放内存的约定;接收者负责释放内存。因此,如果内存返回给托管调用者,CLR/Interop 封送拆收器通常会负责释放在本机调用中分配的内存。

    来自Memory Management with the Interop Marshaler (msdn):

    互操作封送拆收器总是尝试 释放非托管分配的内存 代码。此行为符合 COM 内存管理规则,但不同 来自管理本机 C++ 的规则。

    如果您预期,可能会出现混乱 本机 C++ 行为(无内存 释放)使用平台调用时, 它会自动释放内存 指针。例如,调用 遵循 C++ 中的非托管方法 DLL 不会自动释放任何 记忆。

    运行时总是使用 CoTaskMemFree 方法来释放内存。 如果您正在使用的内存是 未使用 CoTaskMemAlloc 分配 方法,您必须使用 IntPtr 和 使用手动释放内存 适当的方法。

    【讨论】:

      【解决方案2】:

      .NET 2.0 Interoperbility Recipes 这本书看起来很方便。它似乎同意 Arnshea 关于 CoTaskMemFree 的说法。

      【讨论】:

        【解决方案3】:

        我几乎 100% 确定 CLR 不会自动释放分配给测试的内存(如果是 PInvoke 我会 100% 确定)。原因是,CLR 是如何首先知道您用于分配内存的?即它没有。

        更安全的写这个函数的方式如下

        void MemAlloc(ref IntPtr arrayPtr, int membercount)
        

        收到回电后,您可以执行以下操作。

        var count = GetTheCount();
        var arrayPtr = IntPtr.Zero;
        obj.MemAlloc(ref arrayPtr, count);
        byte[] test = MarshalThePtrToByteArray(arrayPtr, count);
        Marshal.FreeCoTaskMem(arrayPtr);
        

        这是 MarashalThePtrToByteArray 的快速而肮脏的实现

        byte[] MarashalThePtrToByteArray(IntPtr ptr, int count) {
          byte[] arr = new byte[count];
          for ( int i = 0; i < count; i++ ) {
            arr[i] = (byte)Marshal.PtrToStructure(ptr, typeof(byte));
            ptr = new IntPtr(IntPtr.ToInt64() + Marshal.SizeOf(typeof(byte)));
          }
          return arr;
        }
        

        【讨论】:

        • 对于 COM 互操作,CLR/互操作封送拆收器使用 COM 约定释放返回给调用者的内存,只要内存是使用 CoTaskMemAlloc() 分配的。
        • @Arnshea,但是 CLR 怎么知道它是用 CoTaskMemAlloc 分配的呢?它不能,而且许多 COM 实现不使用 CoTaskMemAlloc 处理数据。
        • 是的。在这种情况下,当他实现本地方法时,他应该使用 CoTaskMemAlloc() 而不是 new/mallow()。然后互操作封送拆收器将负责释放内存。
        • @Arnshea,我不同意。 CLR 无法知道他是否使用了 CoTaskMemAlloc。根本没有办法可靠地判断,因此我相信 CLR 不能可靠地进行免费/非免费调用。
        • 互操作封送拆收器将对参数调用 CoTaskMemFree()(请参阅我在答案中链接的 msdn 文章)。
        【解决方案4】:

        将它包装在一个实现 IDisposable 的对象中,并确保 C# 包装器被释放。

        Here's a blog I wrote 关于实现 IDisposable 的简单方法。

        【讨论】:

        • 使用 IDisposable 是一个很好的资源管理策略,但是如何实现 Dispose 方法来真正释放内存呢?
        猜你喜欢
        • 1970-01-01
        • 2018-09-05
        • 2017-02-13
        • 1970-01-01
        • 2017-07-31
        • 2020-07-06
        • 2016-03-04
        • 1970-01-01
        相关资源
        最近更新 更多