我正在开发一个 C#、UWP 10 解决方案,它使用快速、连续的读/写循环与网络设备进行通信。API 提供的 StreamSocket 似乎工作得很好,直到我意识到存在内存泄漏:堆中有一个堆积,Task<uint32>
大约每分钟数百个。
无论我是在 中使用普通的旧while (true)
循环,还是使用带有 TPL 数据流async Task
的自发布(根据这个答案),结果都是一样的。ActionBlock<T>
如果我消除从套接字读取并专注于写入,我能够进一步隔离问题:无论我使用该DataWriter.StoreAsync
方法还是更直接的方法StreamSocket.OutputStream.WriteAsync(IBuffer buffer)
,问题仍然存在。此外,添加.AsTask()
到这些没有任何区别。
即使垃圾收集器运行,这些Task<uint32>
' 也不会从堆中删除。所有这些任务都是完整的 ( RanToCompletion
),没有错误或任何其他表明“尚未准备好被回收”的属性值。
在这个页面上似乎有一个提示我的问题(从托管世界到非托管世界的字节数组阻止内存释放),但规定的解决方案似乎非常明显:解决这个问题的唯一方法是将所有通信逻辑写入C++/CX。我希望这不是真的;当然,其他 C# 开发人员已经成功实现了无内存泄漏的持续高速网络通信。而且微软肯定不会发布一个只有在 C++/CX 中没有内存泄漏的情况下才能工作的 API
编辑
根据要求,一些示例代码。我自己的代码有太多层,但是可以通过这个 Microsoft 示例观察到一个更简单的示例。我做了一个简单的修改,循环发送 1000 次以突出问题。这是相关代码:
public sealed partial class Scenario3 : Page
{
// some code omitted
private async void SendHello_Click(object sender, RoutedEventArgs e)
{
// some code omitted
StreamSocket socket = //get global object; socket is already connected
DataWriter writer = new DataWriter(socket.OutputStream);
for (int i = 0; i < 1000; i++)
{
string stringToSend = "Hello";
writer.WriteUInt32(writer.MeasureString(stringToSend));
writer.WriteString(stringToSend);
await writer.StoreAsync();
}
}
}
启动应用程序并连接套接字后,堆上只有一个实例Task<UInt32>
。单击“SendHello”按钮后,有 86 个实例。第二次按下后:129 次。
编辑#2 在运行我的应用程序(使用紧密循环发送/接收)3 小时后,我可以看到肯定存在问题:50 万个任务实例,它们永远不会被 GC 处理,并且应用程序的进程内存从初始值上升46 MB 到 105 MB。显然这个应用程序不能无限期地运行。 但是...这仅适用于在调试模式下运行。如果我在发布模式下编译我的应用程序,部署并运行它,则不会出现内存问题。我可以让它整夜运行,很明显内存管理得当。结案。