1、现象描述
- 某些应用程序频繁调用 malloc函数申请内存空间,且申请空间的大小差别比较大,使用完成后通过 free函数释放内存空间,但内存空间依然缓存在glibc中,没有归还操作系统,导致系统内存不足。
2、原因分析
Glibc中进程的内存分配由两个系统调用完成: brk 和 mmap:
- brk 是将数据段(.data)的最高地址指针 _edata 往高地址推; brk 分配的内存需要等到高地址内存释放以后才能释放;
假设先后通过 brk 申请了A和B两块内存,在B释放之前, A是不可能释放的,仍然被进程占用,通过TOP查看疑似“内存泄露”。 - mmap 是在进程的虚拟地址空间中申请一块空闲的空间。 mmap 分配的内存由 munmap 释放,内存释放时将立即归还操作系统。
默认情况下,大于等于128KB的内存分配会调用 mmap/mummap,小于128KB的内存请求调用 brk,但可以通过修改M_MMAP_THRESHOLD 值来调整。
另外, Glibc2.29有一个新特性: M_MMAP_THRESHOLD 可以动态调整。
M_MMAP_THRESHOLD的值在128KB到32MB(32位机)或者64MB(64位机)之间动态调整,如:当申请并释放一个大小为2MB的内存后, M_MMAP_THRESHOLD的值被调整为2M到2M + 4K之间的一个值。因此,当应用程序中申请的内存空间数量多,且先后申请的内存空间的大小变化比较大时,再申请一段大的内存后, M_MMAP_THRESHOLD的值被调大,后续的内存申请空间大小 < M_MMAP_THRESHOLD时将使用 brk 申请,而 brk 需要等到高地址内存释放以后,低地址内存才能释放。当应用程序没有释放高地址内存时,就导致大量低地址内存空间不能及时释放,从而产生“内存空洞”,导致系统内存不足。
3、解决方法
- 在进程启动时,使用 int mallopt(int param, int value) 函数显式地设置 M_MMAP_THRESHOLD 的值为128K,关闭M_MMAP_THRESHOLD动态调整特性【图1-1】
- 优化应用程序中内存管理方式,不要频繁申请、释放内存空间,减少内存碎片。
【图1-1】