简体   繁体   English

异步套接字高内存使用率和可能的泄漏

[英]Async Socket High Memory Usage and Possible Leak

I have a server application rewrite underway and am puzzled by the memory usage of the application. 我正在重写一个服务器应用程序,并且对该应用程序的内存使用感到困惑。 The earlier version was written with TcpListener while the new one is plain old Socket . 较早的版本是用TcpListener编写的,而新版本是普通的Socket This is mostly for performance and stability reasons which are secondary to this question and even this issue. 这主要是出于性能和稳定性的原因,而这是该问题甚至该问题的第二要因。

As mentioned, everything is heavily async'd with AcceptAsync , SendAsync , and ReceiveAsync . 如前所述,一切都与AcceptAsyncSendAsyncReceiveAsync严重异步。 On top of that, I use ThreadPool.QueueUserWorkItem for utility tasks such as the initial kick-off for AcceptAsync and keeping the next AcceptAsync queued, the call after processing to write back to the Socket , and the call cleaning up disconnected clients. 最重要的是,我将ThreadPool.QueueUserWorkItem用于实用程序任务,例如AcceptAsync的初始启动并保持下一个AcceptAsync排队,处理回写给Socket的调用以及清理断开连接的客户端的调用。 Further, there are a series of events that I fire with BeginInvoke and EndInvoke . 此外,我使用BeginInvokeEndInvoke触发了一系列事件。

The detection for those disconnects as well as the main driver for data availability are handled by a custom class that I call AvailabilityNotifier that peaks on a ReceiveAsync as well as detecting for SocketAsyncEventArgs.BytesTransferred being zero which fires a Disconnect event. 这些断开连接的检测以及数据可用性的主要驱动程序由一个自定义类处理,我将这个自定义类称为AvailabilityNotifier ,该类在ReceiveAsync达到峰值,并检测SocketAsyncEventArgs.BytesTransferred为零,这会引发Disconnect事件。

The throughput of the application is good, and there's almost zero (relatively speaking) lock contention thanks to a healthy usage of System.Collections.Concurrent objects. 应用程序的吞吐量很好,而且由于System.Collections.Concurrent对象的正常使用,锁争用几乎为零(相对而言)。 However, it clings to memory like a predator clinging to a kill. 但是,它像捕食者坚持杀死一样紧贴记忆。

I've debugged to verify my internal collections are getting cleared, the client sockets are being shutdown and disposed of, and utilizing a buffer pool instead of creating new buffers for each read. 我已进行调试,以验证是否清除了内部集合,正在关闭并丢弃客户端套接字,并利用缓冲池而不是为每次读取创建新的缓冲区。 Running a test application that ultimately performs 1,000 connections (100 concurrent) and sends/receives 100,000 messages bloats the server process memory to around 800 MB and it never goes down even after Windows clears any TIME_WAIT s that might have happened. 运行一个最终执行1,000个连接(并发100个)并发送/接收100,000条消息的测试应用程序,会使服务器进程的内存膨胀到800 MB左右,即使Windows清除了可能发生的TIME_WAIT之后,它也永远不会崩溃。 I know for sure the diposal code is firing thanks to a ton of ObjectDisposedException and null exception catch blocks that you can see in the linked github below. 我可以肯定,由于大量的ObjectDisposedException和null异常catch块(这些异常块可以在下面的链接的github中看到),触发了代码的发布。

I say all that without quoted code as it's quite long for a post here so here's a github: https://github.com/hoagsie/TcpServer . 我说的所有这些都没有引用代码,因为这里的帖子已经很长了,所以这里是一个github: https : //github.com/hoagsie/TcpServer The Program.cs and ClientProgram.cs is provided as well if you'd want to run it yourself but the main action is in NetworkServer.cs and AvailabilityNotifier.cs . 如果您想自己运行它,也可以提供Program.csClientProgram.cs ,但是主要操作是在NetworkServer.csAvailabilityNotifier.cs The sample server I have running also has a WCF service it talks to but is just the standard WCF project with literally no modifications. 我正在运行的示例服务器还具有与之通信的WCF服务,但它只是标准的WCF项目,几乎没有任何修改。 I just needed it to match a sample scenario. 我只需要它来匹配示例场景。

I'm also not sure if it matters on some level, but I do build this in x64 mode rather than AnyCPU/x86. 我也不确定在某种程度上是否重要,但是我确实以x64模式而不是AnyCPU / x86来构建它。 This is mostly for resource consumption opportunity on the target server it will be going on, but I haven't noticed a difference in behavior with regard to this issue in either x86 or x64. 这主要是为了将要进行的目标服务器上的资源消耗,但是我没有注意到x86或x64在此问题上的行为有所不同。

EDIT: 编辑:

A coworker pointed out the Snapshot tool in Visual Studio. 一位同事指出了Visual Studio中的“快照”工具。 I had never seen this before and it displayed things differently from what I had been using, which was dotTrace. 我以前从未见过,它显示的内容与我以前使用的东西不同,即dotTrace。 It pointed to a ton of allocations around the SocketAsyncEventArgs object which makes sense, but they kept building and building. 它指出了在SocketAsyncEventArgs对象周围的大量分配,这很有意义,但是它们一直在构建。 I looked at its member list again and discovered it had a Dispose method. 我再次查看了它的成员列表,发现它具有Dispose方法。 My issue has gone away. 我的问题不见了。 I didn't realize that was an IDisposable object. 我没有意识到那是一个IDisposable对象。

A coworker pointed out the Snapshot tool in Visual Studio. 一位同事指出了Visual Studio中的“快照”工具。 I had never seen this before and it displayed things differently from what I had been using, which was dotTrace. 我以前从未见过,它显示的内容与我以前使用的东西不同,即dotTrace。 It pointed to a ton of allocations around the SocketAsyncEventArgs object which makes sense, but they kept building and building. 它指出了在SocketAsyncEventArgs对象周围的大量分配,这很有意义,但是它们一直在构建。 I looked at its member list again and discovered it had a Dispose method. 我再次查看了它的成员列表,发现它具有Dispose方法。 My issue has gone away. 我的问题不见了。 I didn't realize that was an IDisposable object. 我没有意识到那是一个IDisposable对象。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM