如何阻止异步tcp .NET代码耗尽整个系统的资源

在我的一些异步tcp服务器代码中,偶尔会发生错误,导致进程占用整个系统的内存。 在查看日志,事件查看器和一些MS文档时,如果“调用应用程序多次向同一客户端调用异步IO,那么如果远程客户端停止其结束,您可能会看到堆碎片和私有字节增加” / O“导致内存使用量激增以及System.Threading.OverlappedData结构和字节数组的固定。

知识库文章提出的解决方案是“设置未完成缓冲区(发送或接收)与其异步IO的上限。”

怎么做到这一点? 这是指发送到BeginRead的byte []吗? 那么解决方案只是用信号量包装访问byte []?

编辑:信号量控制访问字节缓冲区或只是具有静态大小的字节缓冲池是两种常见的解决方案。 我仍然存在的一个问题是,当这个异步客户端问题发生时(实际上它可能是一些奇怪的网络事件)有信号量或字节缓冲池将阻止我耗尽内存,但它无法解决问题。 我的缓冲池可能会被问题客户端吞噬,实际上锁定了正确的函数合法客户端。

编辑2:遇到了这个伟大的答案 。 基本上它显示了如何手动取消固定对象。 虽然异步TCP代码可以固定到幕后运行时规则,但是可以通过在使用前明确地固定每个缓冲区来覆盖它,然后在块的末尾或者在finally中取消固定。 我现在想弄明白……

解决该问题的一种方法是预分配异步通信中使用的缓冲区和其他数据结构。 如果在启动时预先分配,则不会出现碎片,因为内存自然会驻留在堆的同一区域中。

我建议使用添加到.Net 3.5 SP1的ReceiveAsync / SendAsync API,它允许您缓存或预先分配SocketAsyncEventArgs结构和存储在SocketAsyncEventArgs.Buffer属性中的内存缓冲区,这与仅允许缓存的旧版BeginXXX / EndXXX API不同。预分配内存缓冲区。

使用旧的API也会产生大量的CPU成本,因为API会在内部一次又一次地创建Windows重叠的I / O结构。 在新的API中,这发生在SocketAsyncEventArgs ,因此通过汇集这些对象,CPU成本仅支付一次。

关于pinning的更新:pinning是有原因的,即防止GC在碎片整理期间移动缓冲区。 通过手动取消固定,可能会导致内存损坏。