c# - C# 应用程序结束时分配的内存不会被释放
问题描述
我有一个围绕 Python 模块的 C++ 包装器,它在 C++ 中完美运行。那就是没有任何内存泄漏。我在 C 中公开了 C++ 功能,并在 C# 应用程序中使用了它。但是,我注意到每当我结束 C# 应用程序时,分配的内存都不会被释放!
具有讽刺意味的是,一切都是在 Python 中完成的,而在 C++ 中没有内存分配,它只是一系列调用 Python 对应物的方法,而在 Python 中,所有内存都由解释器管理。Python 模块基本上运行一个无限循环,其中使用网络摄像头提要,处理图像并将其发送回客户端(C#、C++)。Python中有一个Stop
方法,它简单地设置一个值,在python中结束这个循环,所以当那个循环结束时,实际上一切都结束了,因此被释放了。
旁注:
您看到的内存与加载到内存中的模型有关,并且该模型由Pytorch
(Python 中的深度学习框架)管理,因此在 Python 部分,我们只需要调用其他托管代码。
C++ 只是调用这些方法,我在 Python 或 C++ 中没有任何问题,但是当涉及到 C# 时,分配的内存不会被释放!
万一这很重要,这是我DLLImport
对 C# 的陈述:
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int Initialize(bool showFeed);
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Start(bool async);
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void Stop();
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetCpuAffinity(int mask);
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool GetVideoFeedDbgStatus();
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SetVideoFeedDbgStatus(bool status);
public delegate void CallbackDelegate(bool status, string message, IntPtr img, int rows, int cols);
[MethodImplAttribute(MethodImplOptions.InternalCall)]
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void AddCallback(IntPtr fn);
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void RemoveCallback(IntPtr fn);
[DllImport(@"Core_DLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetCallbacks_str();
这是 C# 回调:
int op=0;
public void callback01(bool status, string id, IntPtr img_ptr, int rows, int cols)
{
this.status = status;
this.id = id;
int[] sizes = { rows, cols };
img = new Mat(sizes, MatType.CV_8UC3, img_ptr);
// this is to retain the image, as the callback is so fast
// the time c# will draw it, it will be invalidated and thus you'd get
// distorted image. sowe make a deep copy here so we can work with images
// at our own pace!
img2 = new Mat();
img.CopyTo(img2);
//this allows to execute all interop calls under one thread
switch (op)
{
case 1:
Stop();
//MessageBox.Show("done.");
t.Abort();
break;
case 2:
MessageBox.Show(GetVideoFeedDbgStatus().ToString());
break;
default:
break;
}
//resetting the flag
op = 0;
}
它是如何开始的:
Thread t;
private void btnRunService_Click(object sender, EventArgs e)
{
tmrFetchStatus.Start();
t = new Thread(new ThreadStart(() =>
{
RunService();
}));
t.IsBackground = true;
t.Start();
}
void RunService()
{
btnInit_Click(null, null);
Start(chkboxAsync.Checked);
}
private void btnInit_Click(object sender, EventArgs e)
{
Initialize(chkbxIShowFeed.Checked);
SetUpCallback();
tmrFetchStatus.Enabled = true;
}
这就是它的结束方式:
private void btnStop_Click(object sender, EventArgs e)
{
op = 1;
}
C 暴露的方法是这样的:
extern "C"
{
Core* core = nullptr;
CORE_API int Initialize(bool showFeed)
{
return CreateHandle(showFeed) != nullptr ? 0 : 1;
}
CORE_API void Start(bool async)
{
core = reinterpret_cast<Core*>(GetHandle());
core->Start(async);
}
CORE_API void Stop(void)
{
core = reinterpret_cast<Core*>(GetHandle());
core->Stop();
}
CORE_API void AddCallback(CCallbackFn callback)
{
core->AddCallback_C_tmp(callback);
}
CORE_API void RemoveCallback(CCallbackFn callback)
{
core->RemoveCallback_C_tmp(callback);
}
std::string callbackLst;
CORE_API const char* GetCallbacks_str(void)
{
core = reinterpret_cast<Core*>(GetHandle());
auto results = core->GetCallbacks_C_tmp();
// this is shared, so clear it each time.
callbackLst = "";
for (auto pair : results)
{
callbackLst += pair.first + "\r\n";
}
//size_t size = callbackLst.size() * sizeof(char) + 1;
//char* output = new char[size];
//strcpy_s(output, size, callbackLst.c_str());
return callbackLst.c_str();
}
CORE_API CCallbackFn* GetCallbacks()
{
core = reinterpret_cast<Core*>(GetHandle());
auto results = core->GetCallbacks_C_tmp();
size_t size = results.size() * sizeof(CCallbackFn) + 1;
CCallbackFn* callback_list = new CCallbackFn[size];
int i = 0;
for (auto pair : results)
{
callback_list[i] = pair.second;
}
return callback_list;
}
}
在使用/调用 Python 模块的 C++ 部分中:
this->serviceUtilsModule = py::module::import("SomeModule");
this->cls = this->serviceUtilsModule.attr("SomeClass");
//...
this->startFunc = this->obj.attr("start");
this->startFuncAsync = this->obj.attr("start_async");
this->stopFunc = this->obj.attr("stop");
...
}
//...
CORE_API void Core::Start(bool async)
{
try
{
if (async)
{
this->startFuncAsync();
}
else
{
this->startFunc();
}
}
catch (std::exception ex)
{
std::cout << ex.what() << std::endl;
}
}
CORE_API void Core::Stop(void)
{
this->stopFunc();
}
这可能是什么原因?我应该做什么我不是?这是一个小演示,展示了 C++ 应用程序和 C# 之间的区别(C++ 没有任何内存问题,而在 C# 中,我必须手动释放内存!):https ://imgur.com/a /eaW9AYT
这是第二个剪辑,显示了内部的 C# 应用程序(更多调试信息):https ://imgur.com/a/wL6UUOV
C++ 应用程序只需start
s,然后在 10 秒后停止调用,当存在时释放所有内存。但是,在 C# 中,这不会发生,如您所见。
解决方案
我发现了这个问题。该问题似乎是由终止自身内部的线程引起的,该线程在其内部callback01
运行,假设线程 2,我们试图终止这个线程:
...
switch (op)
{
case 1:
Stop();
//MessageBox.Show("done.");
t.Abort();
break;
case 2:
MessageBox.Show(GetVideoFeedDbgStatus().ToString());
break;
default:
break;
}
Stop 确实停止了 python 循环,但它并没有尽快释放内存!我相信是因为没有卸载python模块,python对象仍然存在,因此保留了它的内存。但是,关闭 C# 应用程序会释放内存。
由于线程被标记为背景,关闭应用程序也应该终止它。
旁注:
我想知道为什么我在 C# 中没有遇到任何异常!
推荐阅读
- maven - 我有工作正常的方法,但是当我从方法返回驱动程序时,它显示以下错误?
- graphql - GraphQL API 保护分支
- excel - 将“Aug 2 2019 10:22am”等日期字符串转换为 Microsoft Excel 中的有效日期
- quantum-computing - 给定随机量子行为,如何哄骗量子位在量子计算机中产生有意义的输出?
- javascript - 赛普拉斯:根据其他字段加载我的下拉菜单时选择随机选项
- python - “TypeError:'int' 对象不可迭代”
- visual-c++ - 如何将 IUSR 的权限设置为 mysql,以便我的 C++ cgi 脚本不返回错误 502.2?
- ios - 为什么我的通知事件没有在 Cordova 中触发?
- html - 我无法使用物化 css 在输入字段中输入任何文本
- php - 当 PHP Post body 不为 null 时,它是 null 和 Retrofit