【问题标题】:free allocated memory when interoping c++互操作 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 看看这个链接上的例子请...我也在学习....

标签: c# c++ c memory-management pinvoke


【解决方案1】:

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

__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,则有两种选择:

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

  2. 所有权从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 - 非托管内存分配很复杂,我不是说“不要这样做”,而是说“谨慎行事” - 而且,从性能方面来说,什么在小范围内有效测试项目在大型应用程序中的工作方式不同。直到您在真正的完整应用中衡量性能之前,您一无所知
猜你喜欢
  • 2013-02-28
  • 1970-01-01
  • 1970-01-01
  • 2017-06-21
  • 2012-07-27
  • 1970-01-01
  • 2011-03-29
  • 2012-01-16
  • 2013-11-22
相关资源
最近更新 更多