C++/C# 应用程序中的内存泄漏
Posted
技术标签:
【中文标题】C++/C# 应用程序中的内存泄漏【英文标题】:Memory Leak in C++/C# application 【发布时间】:2014-10-28 08:18:16 【问题描述】:我有一个使用 C++ DLL 与佳能相机通信的应用程序,此 C++ DLL 中的方法是从 C# 应用程序调用的。我在应用程序中看到的是,拍照时,内存当然会增加。在我关闭“图像捕获窗口”后,应用程序仍然拥有与捕获所有图像时相同的内存量。
由于我的应用程序存在多层 WPF 用户控件,我认为“图像预览用户控件”无法进行垃圾收集,因为其他控件订阅了从该控件触发的事件。经过一番谷歌搜索,我决定在事件中实现Weak Reference Pattern
。
//Source code found here: http://paulstovell.com/blog/weakevents
public sealed class WeakEventHandler<TEventArgs> where TEventArgs : EventArgs
private readonly WeakReference _targetReference;
private readonly MethodInfo _method;
public WeakEventHandler(EventHandler<TEventArgs> callback)
_method = callback.Method;
_targetReference = new WeakReference(callback.Target, true);
public void Handler(object sender, TEventArgs eventArgs)
var target = _targetReference.Target;
if (target != null)
var callback =
(Action<object, TEventArgs>)
Delegate.CreateDelegate(typeof (Action<object, TEventArgs>), target, _method, true);
if (callback != null)
callback(sender, eventArgs);
所以,如果我忘记取消订阅某些事件,GC 无论如何都会收集它们。经过一些测试,这种方法不起作用,所以我决定使用Redgate ANTS Memory Profiler
我拍了三张快照:
-
拍照前
我拍了 4 张照片后
wpf 控制器销毁后
比较快照 1 和 3 时的结果:
正如您所见,分配的非托管内存量是这里的大问题。我的第一个想法是当“图像捕获窗口”关闭时,C++ DLL 没有释放分配的内存。
我是否正确地认为问题出在 C++ 插件中?我可以排除 C# 应用程序吗?据我所知,所有用 .NET 编写的代码都是托管内存。
基于此处的评论是图像如何从 C++ 插件到达 C# 插件:
来自 C++ 插件的回调是这样的:
_resultcallback(img->GetImageInfo().Data, img->GetImageInfo().Width, img->GetImageInfo().Height, img->GetImageInfo().BPP);
以及C#端接收图片的方法:
private void OnResultImageCallback(IntPtr imagePtr, int width, int height, int bitsPerPixel)
_state = CameraState.InitializedStandby;
_cbResultData.Width = width;
_cbResultData.Height = height;
_cbResultData.BitsPerPixel = bitsPerPixel;
int memSize = bitsPerPixel * width * height / 8;
_cbResultData.data = new byte[memSize];
Marshal.Copy(imagePtr, _cbResultData.data, 0, memSize);
_deleteAllocatedImageFunction(imagePtr);
if (ImageCaptured != null)
ImageCaptured(_cbResultData.data, _cbResultData.Width, _cbResultData.Height, _cbResultData.BitsPerPixel);
_cbResultData.data = null;
我还有一种方法可以清除我的 C++ 中分配的内存,它接收一个像这样的字节指针:
BOOL CanonEDSDKWnd::ClearImageBuffer(BYTE* img)
_debug->Write(_T("CanonEDSDKWnd::ClearImageBuffer"));
delete[] img;
return TRUE;
在 C# 代码中使用回调中的 IntPtr
调用
_deleteAllocatedImageFunction(imagePtr);
【问题讨论】:
图像如何从 C++ 库到达您的 c# 代码?非托管方法的调用是如何完成的?您如何在托管端存储图像?等等,等等......如果没有关于代码的知识,很难说任何有用的东西。 请看我的编辑。我认为它应该是必不可少的部分。 我很确定回调中img
指向的对象必须以某种方式被释放。您应该有一个执行 delete img
的 c++ 函数,并且应该在将数据复制到托管存储后调用它。
我有这样的方法。请看我的编辑。这种做法有错吗?
让我们continue this discussion in chat。
【参考方案1】:
我认为你的回调函数应该如下所示:
C++ 方面:
_resultcallback(
img // extend the signature
img->GetImageInfo().Data,
img->GetImageInfo().Width,
img->GetImageInfo().Height,
img->GetImageInfo().BPP
);
C#端:
private void OnResultImageCallback(IntPtr img, IntPtr imagePtr, int width, int height, int bitsPerPixel)
_state = CameraState.InitializedStandby;
_cbResultData.Width = width;
_cbResultData.Height = height;
_cbResultData.BitsPerPixel = bitsPerPixel;
int memSize = bitsPerPixel * width * height / 8;
_cbResultData.data = new byte[memSize];
Marshal.Copy(imagePtr, _cbResultData.data, 0, memSize);
_deleteAllocatedImageFunction(img);
if (ImageCaptured != null)
ImageCaptured(_cbResultData.data, _cbResultData.Width, _cbResultData.Height, _cbResultData.BitsPerPixel);
_cbResultData.data = null;
【讨论】:
我认为这行不通,因为img
是unique_ptr
以上是关于C++/C# 应用程序中的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章