【发布时间】:2016-01-02 19:03:33
【问题描述】:
我正在开发一个 C#、UWP 10 解决方案,该解决方案使用快速、连续的读/写循环与网络设备进行通信。 API 提供的 StreamSocket 似乎工作得很好,直到我意识到存在内存泄漏:堆中有 Task<uint32> 的积累,每分钟数百个。
无论我在async Task 中使用普通的旧while (true) 循环,还是使用带有TPL 数据流的自发布ActionBlock<T>(根据this answer),结果都是一样的。
如果我消除从套接字读取并专注于写入,我可以进一步隔离问题:
无论我使用DataWriter.StoreAsync 方法还是更直接的StreamSocket.OutputStream.WriteAsync(IBuffer buffer),问题仍然存在。此外,将.AsTask() 添加到这些没有任何区别。
即使垃圾收集器运行,这些Task<uint32> 也不会从堆中删除。所有这些任务都已完成 (RanToCompletion),没有错误或任何其他表明“尚未准备好回收”的属性值。
似乎在this page 上暗示了我的问题(从托管世界到非托管世界的字节数组阻止了内存的释放),但规定的解决方案似乎非常明显:解决这个问题的唯一方法是编写C++/CX 中的所有通信逻辑。我希望这不是真的;当然,其他 C# 开发人员已经成功实现了无内存泄漏的持续高速网络通信。而且微软肯定不会发布只在 C++/CX 中无内存泄漏工作的 API
编辑
根据要求,一些示例代码。我自己的代码有太多层,但可以使用this Microsoft sample 观察一个更简单的示例。我做了一个简单的修改,循环发送 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'd,以及应用程序的进程内存从最初的 46 MB 增加到 105 MB。显然这个应用程序不能无限期地运行。 但是...这仅适用于在调试模式下运行。如果我在 Release 模式下编译我的应用程序,部署并运行它,则不会出现内存问题。我可以让它整夜运行,很明显内存管理得当。 结案。
【问题讨论】:
-
贴出问题的代码
-
@alexm,请查看发布的代码。谢谢
-
@BCA - 当您在事件处理程序中引用全局对象时,它如何垃圾收集页面?
-
不是页面,而是任务
实例,我认为它代表 StoreAsync() 调用 -
澄清一下:我不希望 Page 或 StreamSocket 收集垃圾。但我希望与 StoreAsync() 关联的任务能够以某种方式清理
标签: c# .net async-await winrt-async stream-socket-client