9

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

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

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

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

编辑2:遇到了这个很棒的答案。基本上它显示了如何手动取消固定对象。虽然异步 TCP 代码将固定保留在幕后运行时规则,但可以通过在使用前显式固定每个缓冲区,然后在块的末尾或在 finally 中取消固定来覆盖它。我现在正试图弄清楚...

4

1 回答 1

4

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

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

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

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

于 2012-05-28T20:40:39.910 回答