1. 原因
“堆” (heap)是一块连续的虚拟地址空间(Virtual Address Space)。用户常用malloc 和 free来申请和释放所需要的空间。 堆经过malloc,free等操作后,堆内产生了很多碎片。碎片的大小一般是物理页的整数倍。通常,用户可使用的地址空间为2到3G,空间包括了代码段、数据段、堆栈、和共享库所需的空间,其中堆占用的空间最大。但是随着碎片数量逐步增加,如果堆中连续的块的大小小于程序所需的空间大小时,系统会产生Out Of Memory。
2. 解决思路
方法一:通过利用其他的用户地址空间来弥补堆空间的不足
方法二:扩张堆的地址空间已满足需求
方法三:应用程序自身的调整
方法四:移植到64位
方法五:利用系统堆回收机制
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
方法一:在32位平台上,每个应用程序即进程都拥有4G的虚拟地址空间,通常内核的所需的1G的地址空间,剩余的3G空间由程序来使用。由程序来使用的地址空间被称为用户地址空间。用户地址空间包括代码段、数据段、堆栈段和共享库所需的空间。能存储数的空间有数据段和堆栈段。由于堆已经无法来满足我们的需求,因此我们可以在数据段或栈段开辟一块空间。
1)我们可以通过定义静态数组来分配一块地址空间,这个大块的全局空间可以用来满足较大的空间请求。(静态数组的例子)。
2)一般栈只使用了系统分配给它的一小段空间,其他的空间我们也可以用来存储数据。比如说Windows提供_alloca函数,此函数可以申请栈中的数据空间。Memory Allocation for Windows,此函数表中提供了所有申请空间的函数,其中栈也可以作为提供空间的一种方法。
方法二:任何平台的地址空间布局(layout)是固定的,但是有些块的大小是可以配置的。比如说堆栈的大小可以通过系统的启动参数来配置。Window 平台可以通过BCDedit 来配置用户地址空间。它使得用户地址空间由2G扩张到3G,同时堆的空间也扩张了。Linux 有扩张堆空间的函数吗?
方法三:如果以上方法都无法解决out of memory,那就得分析这个应用程序申请多大块的空间时出错,出错时堆的碎片情况是什么,这个大块的申请能不能分解成几次小块的申请。
方法四:如果系统中所有的资源都被你充分利用----数据段、堆栈段的空间都被消耗殆尽,程序仍需要大量的数据空间时,建议移植到64位系统。64位系统拥有大量的、充足的地址空间。
方法五:如果所有碎片空间的总和足够满足应用程序之后的需求,建议尝试将更好的堆回收机制打开。Windows提供‘Low-fragmentation Heap‘ (LFH)机制,可以减少碎片数量,因此有较大的连续空间用来分配。
技巧:为了获得连续的大块地址空间,也可以在程序运行一开始就在堆中申请一块较大的空间,为大块数据提供空间。
总结:这些方法是在项目解决out of memory时调研的。本人采用了最简单直接的第二个方案,快速解决了客户的问题。但基于将来的考虑,我们将项目移植到64位平台。
如果您还能提供更好的或更具体的解决方案,不胜感激。非常欢迎评论和探讨。