我是否在一个异步任务中使用一个简单的(while)while(true)循环,或者使用一个自动发布的ActionBlock< T>使用TPL数据流(根据this answer),结果是一样的.
如果我消除了从套接字读取并专注于写入,我可以进一步隔离问题:
无论我使用DataWriter.StoreAsync方法还是使用更直接的StreamSocket.OutputStream.WriteAsync(IBuffer缓冲区),问题仍然存在.此外,将.AsTask()添加到这些没有什么区别.
即使垃圾收集器运行,这些Task< uint32>从来没有从堆中移除.所有这些任务都完成(RanToCompletion),没有任何错误或任何其他属性值,表示“不太准备回收”.
似乎有一个提示我的问题在this page(一个从托管到非托管世界的字节数组,防止释放内存),但规定的解决方案似乎很明显:唯一的办法是将所有通信逻辑写入C/C++X.我希望这不是真的肯定其他C#开发人员已经成功地实现了高速网络连续的连续通信,没有内存泄漏.当然,Microsoft不会发布一个仅在C/C++X中没有内存泄漏的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,应用程序的进程内存从最初的46 MB增加到105 MB.显然这个程序不能无限期地运行.
但是,这只适用于在调试模式下运行.如果我在发布模式下编译我的应用程序,请将其部署并运行,没有内存问题.我可以把它整夜运行,很明显,正在正确地管理内存.
案件关闭.
解决方法
there are 86 instances. After pressing it a 2nd time: 129 instances.
这是完全正常的.还有一个强烈的提示,这里的真正问题是您不知道如何正确解释内存分析器报告.
任务听起来像一个非常昂贵的对象,它有很大的冲击力,一个线程涉及,你可以创建的最昂贵的操作系统对象.但是不是,一个Task对象实际上是一个惩罚对象.它在32位模式下只需要44字节,在64位模式下只有80字节.真正昂贵的资源不是由Task拥有,线程管理器负责处理.
这意味着您可以在GC堆上施加足够的压力以触发集合之前创建大量Task对象.其中约47,000人以32位模式填满第#0段.更多在服务器上,成千上万,其细分更大.
在您的代码片段中,Task对象是您实际创建的唯一对象.因此,您的for(;;)循环通常不能很好地循环,无法看到任务对象的数量减少或限制.
所以这是通常的故事,.NET框架有泄漏的指责,特别是在运行数月的服务器式应用程序中大量使用的这些基本对象类型,永远是夸张的.双重猜测垃圾收集器总是棘手的,您通常只会通过实际使您的应用程序运行几个月,永远不会在OOM失败获得信心.