如何在 C# 中释放在 C++ 中分配的内存
How to free memory in C# that is allocated in C++
我有一个 C++ dll,它正在从相机读取视频帧。这些帧在通过指向调用者(C# 程序)的指针返回的 DLL 中分配。
当 C# 处理完特定的视频帧后,需要对其进行清理。 DLL 接口和内存管理包装在 C# 中的一次性 class 中,因此更容易控制。但是,似乎内存没有得到 freed/released。我的进程的内存占用越来越大,不到一分钟,我在 C++ DLL 中遇到分配错误,因为没有任何内存剩余。
每个视频帧都超过 9 MB。代码比较多,我就简单提供一下allocation/deallocations/types/etc.
首先:在 C++ 中为相机字节分配原始缓冲区。
dst = new unsigned char[mFrameLengthInBytes];
第二:从原始指针传回 DLL 边界作为 unsigned char * 并进入 C# 中的 IntPtr
IntPtr pFrame = VideoSource_GetFrame(mCamera, ImageFormat.BAYER);
return new VideoFrame(pFrame, .... );
所以现在 IntPtr 被传递到 VideoFrame 的 CTOR class。在 CTOR 内部,IntPtr 被复制到 class 的内部成员,如下所示:
IntPtr dataPtr;
public VideoFrame(IntPtr pDataToCopy, ...)
{
...
this.dataPtr = pDataToCopy;
}
我的理解是这是一个浅拷贝,class 现在引用原始数据缓冲区。框架是used/processed/etc。后面把VideoFrameclass处理掉,下面用来清理内存
Marshal.FreeHGlobal(this.dataPtr);
我怀疑问题在于... dataPtr 是一个 IntPtr 而 C# 无法知道底层缓冲区实际上是 9 MB,对吗?有没有办法告诉它此时要释放多少内存?我是否使用了错误的 C# 免费方法?有专门针对这种情况的吗?
您需要在您正在使用的库中调用相应的 "free" 方法。
通过 new
分配的内存是 C++ 运行时的一部分,调用 FreeHGlobal
将不起作用。您需要针对内存调用(一种或另一种方式)delete[]
。
如果这是您自己的库,则创建一个删除内存的函数(例如VideoSource_FreeFrame
)。例如:
void VideoSource_FreeFrame(unsigned char *buffer)
{
delete[] buffer;
}
然后从 C# 调用它,传入你返回的 IntPtr
。
您需要(在 C++ 中)delete dst;
。这意味着您需要提供一个 C# 代码可以调用的 API,例如 FreeFrame(...)
,它正是这样做的。
同意第一个答案。不要在 C# 代码中使用任何魔法、仪式咒语释放它。用 C++ 编写一个释放内存的方法,然后从 C# 代码中调用它。不要养成在一个堆(本机)中分配内存并在另一个堆(托管)中释放它的习惯,这只是个坏消息。
记住 effective C++ 书中的规则之一:在构造函数中分配内存,并在析构函数中释放内存。如果您不能在析构函数中执行此操作,请在 in-class 方法中执行,而不是某些全局(或更糟)的友元函数。
我有一个 C++ dll,它正在从相机读取视频帧。这些帧在通过指向调用者(C# 程序)的指针返回的 DLL 中分配。
当 C# 处理完特定的视频帧后,需要对其进行清理。 DLL 接口和内存管理包装在 C# 中的一次性 class 中,因此更容易控制。但是,似乎内存没有得到 freed/released。我的进程的内存占用越来越大,不到一分钟,我在 C++ DLL 中遇到分配错误,因为没有任何内存剩余。
每个视频帧都超过 9 MB。代码比较多,我就简单提供一下allocation/deallocations/types/etc.
首先:在 C++ 中为相机字节分配原始缓冲区。
dst = new unsigned char[mFrameLengthInBytes];
第二:从原始指针传回 DLL 边界作为 unsigned char * 并进入 C# 中的 IntPtr
IntPtr pFrame = VideoSource_GetFrame(mCamera, ImageFormat.BAYER);
return new VideoFrame(pFrame, .... );
所以现在 IntPtr 被传递到 VideoFrame 的 CTOR class。在 CTOR 内部,IntPtr 被复制到 class 的内部成员,如下所示:
IntPtr dataPtr;
public VideoFrame(IntPtr pDataToCopy, ...)
{
...
this.dataPtr = pDataToCopy;
}
我的理解是这是一个浅拷贝,class 现在引用原始数据缓冲区。框架是used/processed/etc。后面把VideoFrameclass处理掉,下面用来清理内存
Marshal.FreeHGlobal(this.dataPtr);
我怀疑问题在于... dataPtr 是一个 IntPtr 而 C# 无法知道底层缓冲区实际上是 9 MB,对吗?有没有办法告诉它此时要释放多少内存?我是否使用了错误的 C# 免费方法?有专门针对这种情况的吗?
您需要在您正在使用的库中调用相应的 "free" 方法。
通过 new
分配的内存是 C++ 运行时的一部分,调用 FreeHGlobal
将不起作用。您需要针对内存调用(一种或另一种方式)delete[]
。
如果这是您自己的库,则创建一个删除内存的函数(例如VideoSource_FreeFrame
)。例如:
void VideoSource_FreeFrame(unsigned char *buffer)
{
delete[] buffer;
}
然后从 C# 调用它,传入你返回的 IntPtr
。
您需要(在 C++ 中)delete dst;
。这意味着您需要提供一个 C# 代码可以调用的 API,例如 FreeFrame(...)
,它正是这样做的。
同意第一个答案。不要在 C# 代码中使用任何魔法、仪式咒语释放它。用 C++ 编写一个释放内存的方法,然后从 C# 代码中调用它。不要养成在一个堆(本机)中分配内存并在另一个堆(托管)中释放它的习惯,这只是个坏消息。
记住 effective C++ 书中的规则之一:在构造函数中分配内存,并在析构函数中释放内存。如果您不能在析构函数中执行此操作,请在 in-class 方法中执行,而不是某些全局(或更糟)的友元函数。