3

我们有一个运行许多 CORBA 服务器进程的 linux 系统(kubuntu 7.10)。服务器软件使用 glibc 库进行内存分配。linux PC有4G物理内存。出于速度原因,交换被禁用。

在接收到处理数据的请求后,其中一个服务器进程分配一个大数据缓冲区(使用标准 C++ 运算符“new”)。缓冲区大小取决于许多参数,但通常约为 1.2G 字节。它可以达到大约 1.9G 字节。请求完成后,使用“删除”释放缓冲区。

这适用于分配相同大小的缓冲区的多个连续请求,或者如果请求分配的大小比前一个小。内存似乎是空闲的 - 否则缓冲区分配尝试最终会在几个请求后失败。在任何情况下,我们都可以看到使用 KSysGuard 等工具为每个请求分配和释放缓冲区内存。

当请求需要比前一个更大的缓冲区时,就会出现问题。在这种情况下,operator 'new' 会引发异常。就好像从第一次分配中释放的内存无法重新分配,即使有足够的可用物理内存。

如果我在第一次操作后终止并重新启动服务器进程,则第二次请求更大的缓冲区大小会成功。即终止进程似乎将释放的内存完全释放回系统。

任何人都可以解释这里可能发生的事情吗?可能是某种碎片或映射表大小问题吗?我正在考虑用 malloc/free 替换 new/delete 并使用 mallopt 来调整内存释放到系统的方式。

顺便说一句 - 我不确定它是否与我们的问题有关,但服务器使用在每个处理请求上创建和销毁的 Pthreads。

4

3 回答 3

4

如果这是一台 32 位机器,您将拥有 3Gb 的地址空间供您使用 - 1Gb 是为内核保留的。其中,共享库、exe 文件、数据段等将占用相当多的地址空间. 你应该看看/proc/pid/maps地址空间是如何布局的。

很难说有多少物理地址空间可用,所有系统进程、内核和您的其他进程都会占用它。假设这些总和不超过 1Gb,您仍然可以使用 3Gb。

可能发生的是碎片化:

0Gb 3Gb
----------------------------------------------------------------- --------
|东西 | 堆,1.2Gb 分配的东西 | 空闲堆 | 堆栈|
----------------------------------------------------------------- --------

然后释放大对象,但在它们之间分配了一些其他内存,剩下的就是:

0Gb 3Gb
----------------------------------------------------------------- --------------
|东西 | 堆,1.2Gb 空闲 |小对象 | 空闲堆 | 堆栈|
----------------------------------------------------------------- --------------

如果您现在尝试分配一个更大的对象,它将不适合可用的 1.2Gb 空间并且可能也不适合该free heap空间,因为这可能没有足够的空间。

如果您大量使用堆栈,则可能是堆栈增长和占用空间,否则可用于堆空间 - 尽管默认情况下大多数发行版将堆栈限制为 8-10Mb。

使用 malloc/realloc 无济于事。但是,如果您知道所需的最大对象的大小,则可以在启动时保留那么多。那块永远不应该被释放/删除,它应该被重复使用。不过,这是否会让你在其他地方陷入其他麻烦很难说——其他物体的可用空间会变小。

于 2010-03-29T19:08:52.783 回答
0

非常感谢您的回复。进一步的调查表明,碎片确实是问题所在。当第一个请求到达时保留我们将需要的最大缓冲区并为后续请求保留该缓冲区似乎是可行的。

于 2010-03-30T13:52:40.413 回答
0

你的地址空间用完了,所以没有一个块足够大来满足你的分配。

解决办法是运行 64 位操作系统——毕竟现在是 21 世纪!

当然,您需要重新测试您的应用程序的 64 位兼容性(并重新编译等),但从长远来看这是有意义的。如今,4G 对于服务器来说并不是很多内存;一个相当适中的有16-32G。

于 2010-03-30T15:48:48.223 回答