我们有一个运行许多CORBA服务器进程的linux系统(kubuntu 7.10)。 服务器软件使用glibc库进行内存分配。 linux PC有4G物理内存。由于速度原因,交换被禁用。
收到处理数据的请求后,其中一个服务器进程会分配一个大数据缓冲区(使用标准C ++运算符'new')。缓冲区大小取决于许多参数,但通常约为1.2G字节。它可以高达约1.9G字节。请求完成后,使用“删除”释放缓冲区。
这适用于几个连续的请求,这些请求分配相同大小的缓冲区,或者请求分配的大小比前一个小。 内存似乎是免费的 - 否则缓冲区分配尝试最终会在几次请求后失败。 在任何情况下,我们都可以看到使用KSysGuard等工具为每个请求分配和释放缓冲区内存。
当请求需要大于先前的缓冲区时,会出现问题。 在这种情况下,运算符'new'会抛出异常。 这就好像即使有足够的可用物理内存,也无法重新分配从第一次分配中释放的内存。
如果在第一次操作后杀死并重新启动服务器进程,则第二次请求更大的缓冲区大小成功。即杀死进程似乎将释放的内存完全释放回系统。
有人可以解释这里可能会发生什么吗? 可能是某种碎片或映射表大小问题? 我正在考虑用malloc / free替换new / delete,并使用mallopt来调整内存释放到系统的方式。
顺便说一句 - 我不确定它是否与我们的问题有关,但是服务器使用在每个处理请求中创建和销毁的Pthreads。
答案 0 :(得分:4)
如果这是一台32位机器,你将拥有3Gb的地址空间--1Gb是为内核保留的。其中,共享库,exe文件将占用相当多的地址空间,数据段等。您应该查看/proc/pid/maps
以了解地址空间的布局方式。
有多少物理地址空间可用很难说,你的所有系统进程,内核和其他进程都会吃掉它。假设它们的总和不超过1Gb,你仍然可以使用3Gb。
可能发生的事情是碎片化:
0Gb 3Gb ---------------------~------------------------------------ |Stuff | Heap,1.2Gb allocated stuff | free heap | Stack| ---------------------~------------------------------------
然后释放大对象,但在其他一些内存之间已经存在 分配,留下你:
0Gb 3Gb ---------------------~------------------------------------------ |Stuff | Heap,1.2Gb free |small object(s) | free heap | Stack| ---------------------~------------------------------------------
如果您现在尝试分配更大的对象,它将无法容纳在免费的1.2Gb空间中
也许可能不适合free heap
空间,因为这可能没有足够的空间。
如果您正在大量使用堆栈,则可能是堆栈增长和占用空间 否则可以用于堆空间 - 尽管默认情况下大多数发行版将堆栈限制为8-10Mb。
使用malloc / realloc对此没有帮助。但是,如果您知道所需的最大对象的大小,则可以在启动时保留这么多。该作品永远不应该被免费/删除,它应该被重用。是否会让你在其他地方遇到其他问题很难说 - 其他对象可用的空间会变小。
答案 1 :(得分:0)
非常感谢您的回复。 进一步调查表明,碎片确实是问题所在。 保留第一个请求到达时我们将需要的最大缓冲区,并为后续请求保留缓冲区似乎有效。
答案 2 :(得分:0)
你的地址空间不足,所以没有足够大的单块来满足你的分配。
修复是运行64位操作系统 - 毕竟,这是21世纪!
当然,您需要重新测试应用程序的64位兼容性(并重新编译等),但从长远来看这是有道理的。如今,4G并不是服务器的主流;一个相当温和的人有16-32G。