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

时间:2013-05-16 11:45:57

标签: c# .net sockets .net-4.0 .net-4.5

实际上我开发了一个基于tcp的网络服务器,它可以执行大量的读写操作,并且对其业务逻辑使用小于零的CPU,它充当两个端点之间的桥接器。 网络服务器是为.NET 4.5开发的,它使用:

  • IOCP(Socket.xxxAsync)
  • 预先分配的SocketAsyncEventArgs缓冲池(用于SendAsync和ReadAsync)
  • 预分配字节[]的缓冲池(仅用于读取)
  • System.Collections.Concurrent
  • 小于零的锁(好吧,实际上有一个:))
  • 其他一些东西

我的担忧与垃圾收集有关,事实上,据我所知,虽然我避免了内存碎片预分配我需要的所有缓冲区,垃圾收集器检查它们是否必须被收集,因为它们没有被分配到里面大堆。

会更好,而不是分配10.000字节[8192]会更好地分配一个大字节[81920000]并使用ArraySegment来使用切片?

谢谢。

更新

我切换到服务器模式垃圾收集,似乎我的系统比以前工作得更好(实际上我能够在同一台机器上处理高达4GiB的流量与5000个客户端,这意味着10000个套接字)。 我将开始在更多机器上测试架构。

3 个答案:

答案 0 :(得分:0)

您需要注意细分发送和接收数据的方式。当接收数据时,试图将任何缓冲器保持在LOH的最小值以下,即<80Kb。发送也一样。

另一种方法是创建一个固定大小的缓冲池,以便根据需要使用和回收。这样可以避免不断创建和破坏分配,从而避免出现任何内存碎片问题。

与大多数问题一样,最终选择取决于最终解决方案的最佳选择。通过使用小缓冲区可以更容易地避免碎片,但这也会带来相关的性能成本。

答案 1 :(得分:0)

当我开发基于SocketAsyncEventArgs的高性能TCP服务器时,我遇到了类似“内存泄漏”的问题。

问题是当你使用缓冲区(byte[]它被固定时。所以GC实际上无法做任何事情。

我个人写了这个类(不完全是这个):

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; }
}

另一个名为BufferPool的类,其List<Buffer>作为缓冲池(检测死连接和释放池等的逻辑在我的情况下是非常复杂的,所以我跳过内部)。

这样,您可以重用已分配的字节数组作为新操作的缓冲区。

我已经这样实现了,因为我无法限制最大连接数。如果您可以将连接数限制为特定的最大值,则this codeproject article可以提供帮助。

注意:我忘了说在这种情况下无法回收已分配的内存。

答案 2 :(得分:0)

简单情况下的客户端 - 服务器应用程序意味着 N 客户端和 1 服务器。每个客户端请求都应是独立的,并且独立于其他客户端的请求。这允许您使用 N 线程来支持 N 并发用户。

现在,线程不必使用共享资源。他们可以,但它会影响性能。即使您使用乐观(无锁)并发模型,这也不意味着线程不会竞争共享资源。

现在,如果每个线程都有一个单独的缓冲区,则每个线程都使用它自己的内存,并且不同的并发线程之间不存在竞争。如果您有许多用户,这将增加GC的内存碎片和CPU时间。

如果使用大型共享缓冲区,则会增加尝试访问不同线程的共享缓冲区所花费的时间。此外,您还将减少GC处理收集所花费的时间。

<小时/> 就个人而言,我只想在每个线程中使用1个小缓冲区。这有奖金:

  • 简单模型
  • 编写较少的代码,无需同步
  • 您的CPU级别很小,因此无需进行性能优化
  • 此模型更容易横向扩展:添加另一台服务器和负载均衡器。它开箱即用。