简体   繁体   English

网络服务器应用程序中的内存碎片和垃圾回收

[英]Memory Fragmentation and Garbage Collection in network server apps

Actually i developed an tcp based network server that does a lot of read an write and uses less than zero cpu for its business logic, it acts as bridge between two endpoint. 实际上,我开发了基于tcp的网络服务器,该服务器执行大量读写操作,并且其业务逻辑使用小于零的cpu,它充当两个端点之间的桥梁。 The network server is developed for .NET 4.5, it uses: 网络服务器是为.NET 4.5开发的,它使用:

  • IOCP (Socket.xxxAsync) IOCP(Socket.xxxAsync)
  • a Buffer Pool of preallocated SocketAsyncEventArgs (used for SendAsync and ReadAsync) 预分配的SocketAsyncEventArgs的缓冲池(用于SendAsync和ReadAsync)
  • a Buffer Pool of preallocated byte[] (used only for read) 预分配字节[]的缓冲池(仅用于读取)
  • System.Collections.Concurrent System.Collections.Concurrent
  • Less than zero locks (well, actually there is one :)) 少于零的锁(嗯,实际上有一个:))
  • some other stuff 其他一些东西

My concerns are related to the garbage collection, infact, as far i understand, while i avoid the memory fragmentation preallocating all the buffers i need, the garbage collector check if them must be collected or not because they aren't allocated inside the large heap. 就我所知,我的担心与垃圾收集实际上有关,尽管我避免了内存碎片预分配我需要的所有缓冲区,但垃圾收集器检查是否必须收集它们,因为它们没有在大堆中分配。

Would be better, instead to allocate 10.000 byte[8192] would be better allocate a big byte[81920000] and use ArraySegment to use slices? 会更好,而是分配10.000个byte [8192]会更好,分配一个大字节[81920000]并使用ArraySegment来使用切片?

Thank you. 谢谢。

UPDATE 更新

I switched to server mode garbage collection and it seems that my system works better than before (actually i'm able to handle up to 4GiB of traffic with 5000 clients on the same machine, this means 10000 sockets). 我切换到服务器模式的垃圾回收,看来我的系统比以前更好(实际上,我能够在同一台计算机上使用5000个客户端处理多达4GiB的流量,这意味着有10000个套接字)。 I'll start to test the architecture on more machine in short. 简而言之,我将开始在更多计算机上测试该体系结构。

You need to be careful with how you break down data for sending and receiving. 您需要注意如何分解发送和接收的数据。 When receiving data try to keep any buffers below the minimum for the LOH, ie <80Kb. 接收数据时,请尝试使任何缓冲区都低于LOH的最小值,即<80Kb。 Same for sending. 发送相同。

An alternative is to create a pool of buffers of a fixed size to use and recycle as necessary. 一种替代方法是创建一个固定大小的缓冲区池,以根据需要使用和回收。 This avoid the constant creation and destruction of allocation avoiding any memory fragmentation issues. 这避免了分配的不断创建和破坏,避免了任何内存碎片问题。

The final choice, as with most problems, depends on what's optimum for your final solution. 与大多数问题一样,最终选择取决于最终解决方案的最佳选择。 It can be easier to avoid fragmentation by just using small buffers but this can also have an associated performance cost. 仅使用较小的缓冲区就可以更容易避免碎片,但这也会带来相关的性能成本。

I had this "memory leak"-like problem when I was developing a high performance TCP server based on SocketAsyncEventArgs . 在开发基于SocketAsyncEventArgs高性能TCP服务器时,遇到了类似“内存泄漏”的问题。

The problem is when you use a buffer ( byte[] ) it gets pinned . 问题是,当您使用缓冲区( byte[] )时, 它会被固定 So actually GC can not do anything about it. 因此,实际上GC对此无能为力。

Personally I wrote this class (not exactly this): 我个人写了这个课(不完全是这样):

class Buffer
{
    const int BUFFER_SIZE = 8 * 1024;

    public Buffer()
    {
        InUse = false;
        Bytes = new byte[BUFFER_SIZE];
    }

    public bool InUse { get; set; }
    public byte[] Bytes { get; private set; }
}

And another class named BufferPool which has List<Buffer> as a pool of buffers (the logic for detecting dead connections and free their pool and etc is very complicated in my case so I skip the internals). 还有一个名为BufferPool类,它具有List<Buffer>作为缓冲区池(在我的情况下,检测无效连接并释放其池等的逻辑非常复杂,因此我跳过了内部原理)。

That way you can reuse the allocated byte array as a buffer for a new action. 这样,您可以将分配的字节数组作为新操作的缓冲区重用。

I have implemented it this way because I could not restrict the max number of connections. 我之所以这样实现,是因为我无法限制最大连接数。 If you can restrict the number of connections to a specific max then this codeproject article can help. 如果您可以将连接数限制为一个特定的最大值,那么此代码项目文章可以为您提供帮助。

Note: I forgot to say there is no way to reclaim the allocated memory in this case. 注意:在这种情况下,我忘了说没有办法回收分配的内存。

Client-server application in simple case means N clients and 1 server. 在简单情况下,客户端-服务器应用程序意味着N个客户端和1个服务器。 Each of the client requests shall be self-contained and independent from other clients' requests. 每个客户请求均应独立且独立于其他客户的请求。 This allows you to use N thread to support N concurrent users. 这使您可以使用N个线程来支持N个并发用户。

Now, it is essential that threads don't use shared resources. 现在,至关重要的是线程不要使用共享资源。 They could, but it comes as a performance hit. 他们可以,但这是对性能的打击。 Even if you use optimistic (lockless) concurrency model, this doesn't mean threads won't compete for the shared resource. 即使您使用乐观(无锁)并发模型,这也不意味着线程不会竞争共享资源。

Now if you have a separate buffer per thread, each thread uses it's own memory and no competition exist between different concurrent threads. 现在,如果每个线程有一个单独的缓冲区,则每个线程都使用它自己的内存,并且不同的并发线程之间不存在竞争。 If you have many users, this will increase memory fragmentation and CPU time spent by GC. 如果您有很多用户,这将增加内存碎片和GC花费的CPU时间。

If you use a large, but shared buffer, you will increase time spent on trying to get access to the shared buffer for different threads. 如果使用较大但共享的缓冲区,则会增加尝试访问不同线程的共享缓冲区所花费的时间。 And also you will decrease time spent by GC doing collection stuff. 而且,您还可以减少GC进行收集工作所花费的时间。


Personally, I would just use 1 small buffer per thread. 我个人来说,每个线程只使用1个小缓冲区。 This has bonuses: 这有奖金:

  • Simple model 简单模型
  • Less code to write, no synchronization needed 更少的代码编写,不需要同步
  • Your CPU level is small so no need to do performance optimization 您的CPU级别很小,因此无需进行性能优化
  • This model is much easier to scale horizontally: add another server and a load balancer. 这种模型更易于横向扩展:添加另一台服务器和一个负载均衡器。 It's working out of the box. 开箱即用。

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

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