互操作 C++ 时释放分配的内存

Posted

技术标签:

【中文标题】互操作 C++ 时释放分配的内存【英文标题】:free allocated memory when interoping c++ 【发布时间】:2015-10-26 05:58:32 【问题描述】:

填充一个包含字符串的 stuct 数组,我测试了一个发现,在 c# 中它通过指针执行得更快:

struct name
    int intv;
    IntPtr strv;

当通过GetPacksPtr() 实现时:(参见下面的代码/签名) 这就是我编码的方式,但不确定我是否做得对......

说 ArrL=10,000

        DataPack* DataPackArr;
        List<DataPack> DataPackLst = new List<DataPack>(ArrL);
        GetPacksPtr(ArrL, &DataPackArr);

        DataPack* CurrentPack = DataPackArr;
        for (int i = 0; i < ArrL; i++, CurrentPack++)
        
            DataPackLst.Add(new DataPack()  strv = CurrentPack->strv, intv = CurrentPack->intv );
        

我在哪里可以释放分配的内存,如 __stdcall 定义非托管代码必须释放内存,但谁是“按合同”的所有者......这令人困惑,我试图负责释放分配以最小的性能损失

c++

extern "C" __declspec(dllexport) void __stdcall GetPacksPtr(int size, DataPack** DpArrPtr )


    *DpArrPtr = (DataPack*)CoTaskMemAlloc( size * sizeof( DataPack ));
    DataPack CurPackPtr = *DpArrPtr;
    char aStr[]= "abcdefgHi";
    for ( int i = 0; i < size; i++,CurPackPtr++ )
    

        CurPackPtr->IntVal=i;
        CurPackPtr->buffer = (char*)malloc(sizeof(aStr));
        strcpy(CurPackPtr->buffer, aStr);
    



c#

    [DllImport("exported.dll", CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurity]
    public static extern void GetPacksPtr(int RaySize, DataPack** DataPackArr);

【问题讨论】:

如果这只是 c++,你为什么要标记c?你为什么extern "C"?你明白你在做什么吗? @iharob 请参阅将字符串从 C++ API 返回到 C# =>limbioliong.wordpress.com/2011/06/16/… 我不是那个意思,我的意思是 c 不是 c++,你在混合东西。 @iharob 看看这个链接上的例子请...我也在学习.... 【参考方案1】:

是的,这在您手动分配内存时很常见,当然速度更快,您需要另一个(这部分我不知道该说什么,因为您混合了所有内容)c 这样的函数

__declspec(dllexport) void __stdcall FreePacksPtr(int size, DataPack *DpArrPtr)

    for (size_t i = 0 ; i < size ; ++i)
        free(DpArrPtr[i].buffer);
    CoTaskMemFree(DpArrPtr);

然后在您的 C# 中,只要您不再需要指针,只需调用 FreePacksPtr()

注意:您需要 extern "C" 的事实意味着 C# 代码期望从 dll 加载确切的符号,看来您必须指示 microsoft 编译器编译 c 代码而不是 c++,我不是 100% 确定,但微软编译器混合了这些。

【讨论】:

我正在使用extern "C",因为那是我刚刚复制的演示,但我确实测试过,因为我也确实对此提出了质疑..但删除时:Unable to find an entry point named 'GetPacksPtr' in DLL 'G:\.... 是的,这就是我要告诉你的。编译 c++ 程序时,符号会发生变化,因此它可以支持重载以及我不太熟悉的那种东西。也许您只需将源文件扩展名更改为.c 我想这是一个技巧,如果我是对的,编写 cpp,通过声明 extern C 它编译一个 cDll,这就是我获得性能提升的原因......我想知道......跨度> 关于内存管理的故事,总的来说,谢谢你的好答案和代码解决方案。 仍然在想它试图决定一个释放内存的策略,而不是因为你所说的“直到最后”执行它,但当你需要完成工作时也不这样做,就像在后台操作这样的死点而不影响性能,在程序空闲的地方。【参考方案2】:

当您分配非托管内存时,您必须释放它 - 但没有关于谁负责释放的规则。

在非托管世界中,为每个需要释放的资源拥有一个“所有者”是一种常见的策略,这种所有权就是释放对象的责任。

如果函数 A 分配一块内存,然后将指针传递给函数 B,则有两种选择:

    A 仍然是所有者,完成后将释放内存,B 不必处理释放内存但也不能保存指针以供以后使用,因为它可以随时被 A 释放

    所有权从A转移到B,现在B负责释放,B返回后A不能对指针做任何事情,因为它可以随时被B释放

    李>

请注意,函数原型中没有任何东西代表所有权,这一切都是由编写 A 的程序员和编写 B 的程序员之间达成的协议(通常称为“按合同”)

现在,为了让事情变得更复杂,您可以从中分配许多内存块(称为“堆”),当您释放时,您需要释放到用于分配的同一堆中。

有几个堆由 Windows 管理,.net Marshal 类具有释放内存的方法。

malloc 使用的堆不是其中之一,malloc 分配的内存必须通过在调用 malloc 的同一个 dll 中调用 free 来释放。

最后但并非最不重要的一点是,分配和释放内存是您在非托管代码中可以做的最慢的事情之一,如果您经常这样做,您会遇到内存碎片和引用位置等问题(这个答案是足够长的时间而不进入它们)

分配和释放策略对非托管代码的性能有很大影响 - 这不会对您的性能产​​生最小的影响。

【讨论】:

但是正如你所看到的,nir,@iharob 在他的代码中给出了一个解决方案(额外的调用,额外的 cpu 时间)虽然 only 而我结束了对我的使用它可以accure 在程序部分的末尾,并且与托管替代 List to popultae 相比,使用 maloc 和互操作到 dll 而不是托管的性能提高了 x8 倍. 所以这意味着我应该确保我不会导致任何堆碎片......我会解决这个问题,尝试学习如何避免它。感谢您的评论和您的信息! @RajFelix - 使用非托管内存可能更快 - 但您必须衡量对应用程序的实际影响而不是微基准,例如,如果您只在应用程序现在使用的末尾释放更多内存,使用内存会增加操作系统必须将其中一些交换到磁盘的机会,并且进入磁盘非常慢,这将比使用托管内存慢得多 这是一个相当“项目”(研究碎片整理解决方案),当我的程序增长时,我将继续,这样我可以获得更多参数来打孔......(: @RajFelix - 非托管内存分配很复杂,我不是说“不要这样做”,而是说“谨慎行事” - 而且,从性能方面来说,什么在小范围内有效测试项目在大型应用程序中的工作方式不同。直到您在真正的完整应用中衡量性能之前,您一无所知

以上是关于互操作 C++ 时释放分配的内存的主要内容,如果未能解决你的问题,请参考以下文章

本机 C++ 程序在使用 C++/CLI 和 C# 互操作 DLL 启动时崩溃

Clojurescript 中的 Javascript 互操作分配

通过互操作将字符串数组从 C# 传递到 C++

C# 和 C++ 互操作

如何在 C# 和 C++ 之间进行互操作

C#/C++ 回调类(非函数)互操作 - 如何?