网络服务器应用程序中的内存碎片和垃圾收集
本文关键字:内存碎片 网络服务 服务器 应用程序 网络 | 更新日期: 2023-09-27 18:13:57
实际上我开发了一个基于tcp的网络服务器,它做了大量的读和写,并且使用不到零的cpu用于其业务逻辑,它充当两个端点之间的桥梁。网络服务器是为。net 4.5开发的,它使用:
- IOCP (Socket.xxxAsync)
- 预分配SocketAsyncEventArgs缓冲池(用于SendAsync和ReadAsync)
- 预分配字节的缓冲池[](只用于读)
- System.Collections.Concurrent
- 少于0个锁(好吧,实际上有一个:))
- 一些其他的
我的关注点与垃圾收集有关,事实上,据我所知,当我避免内存碎片预分配我需要的所有缓冲区时,垃圾收集器检查它们是否必须被收集,因为它们不在大堆内分配。
会更好,而不是分配10,000字节[8192]会更好地分配一个大字节[81920000]和使用ArraySegment使用切片?
谢谢。
更新
我切换到服务器模式垃圾收集,似乎我的系统比以前工作得更好(实际上我能够在同一台机器上处理多达4GiB的流量和5000个客户端,这意味着10000个套接字)。总之,我将开始在更多的机器上测试这个体系结构。
您需要小心如何分解发送和接收数据。接收数据时,尽量将缓冲区保持在LOH的最小值以下,即80Kb。发送也是一样。
另一种方法是创建一个固定大小的缓冲区池,以便在必要时使用和回收。这避免了分配的不断创建和销毁,避免了任何内存碎片问题。
与大多数问题一样,最终的选择取决于最终解决方案的最佳方案。使用小缓冲区可以更容易地避免碎片,但这也会带来相关的性能成本。我在开发基于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>
作为缓冲池(检测死连接和释放池等的逻辑在我的情况下非常复杂,所以我跳过内部)。
这样就可以重用分配的字节数组作为新操作的缓冲区。
我这样实现是因为我不能限制最大连接数。如果您可以将连接数限制到特定的最大值,那么这篇代码项目文章可以提供帮助。
注意:我忘了说在这种情况下没有办法回收分配的内存
Client-server application在简单的情况下意味着N个客户端和1个服务器。客户的每一项要求都应独立于其他客户的要求。这允许你使用N线程来支持N并发用户。
现在,线程不使用共享资源是很重要的。他们可以这样做,但这会影响他们的表现。即使您使用乐观(无锁)并发模型,这并不意味着线程不会竞争共享资源。
现在,如果每个线程有一个单独的缓冲区,每个线程使用它自己的内存,不同并发线程之间不存在竞争。如果您有很多用户,这将增加内存碎片和GC花费的CPU时间。
如果您使用一个大的共享缓冲区,那么您将增加为不同线程访问共享缓冲区所花费的时间。而且你还可以减少GC做收集工作所花费的时间。
就我个人而言,我只会为每个线程使用1个小缓冲区。这有奖励:
- <
- 简单模型/gh>
- 代码编写少,不需要同步
- 你的CPU级别很小,所以不需要做性能优化
- 这个模型更容易横向扩展:添加另一个服务器和负载均衡器。它是开箱即用的。