Glibc内存管理--ptmalloc2源代码分析(一)

1. 问题

项目组正在研发的一个类似数据库的NoSql系统,遇到了Glibc的内存暴增问题。现象如下:在我们的NoSql系统中实现了一个简单的内存管理模块,在高压力高并发环境下长时间运行,当内存管理模块的内存释放给C运行时库以后,C运行时库并没有立即把内存归还给操作系统,比如内存管理模块占用的内存为10GB,释放内存以后,通过TOP命令或者/proc/pid/status查看进程占用的内存有时仍然为10G,有时为5G,有时为3Getc,内存释放的行为不确定。

我们的NoSql系统中的内存管理方式比较简单,使用全局的定长内存池,内存管理模块每次分配/释放2MB内存,然后分成64KB为单位的一个个小内存块用hash加链表的方式进行管理。如果申请的内存小于等于64KB时,直接从内存池的空闲链表中获取一个内存块,内存释放时归还空闲链表;如果申请的内存大于64KB,直接通过C运行时库的mallocfree获取。某些数据结构涉及到很多小对象的管理,比如Hash表,B-Tree,这些数据结构从全局内存池获取内存后再根据数据结构的特点进行组织。为了提高内存申请/释放的效率,减少锁冲突,为每一个线程单独保留8MB的内存块,每个线程优先从线程专属的8MB内存块获取内存,专属内存不足时才从全局的内存池获取。

系统中使用的网络库有独立的内存管理方式,并不从全局内存池中分配内存,该网络库在处理网络请求时也是按2M内存为单位向C运行时库申请内存,一次请求完成以后,释放分配的内存到C运行时库。

在弄清楚了系统的内存分配位置以后,对整个系统进行了内存泄露的排查,在解决了数个内存泄露的潜在问题以后,发现系统在高压力高并发环境下长时间运行仍然会发生内存暴增的现象,最终进程因OOM被操作系统杀掉。

为了便于跟踪分析问题,在全局的内存池中加入对每个子模块的内存统计功能:每个子模块申请内存时都将子模块编号传给全局的内存池,全局的内存池进行统计。复现问题后发现全局的内存池的统计结果符合预期,同样对网络模块也做了类似的内存使用统计,仍然符合预期。由于内存管理不外乎三个层面,用户管理层,C运行时库层,操作系统层,在操作系统层发现进程的内存暴增,同时又确认了用户管理层没有内存泄露,因此怀疑是C运行时库的问题,也就是Glibc的内存管理方式导致了进程的内存暴增。

问题范围缩小了,但有如下的问题还没有搞清楚,搞不清楚这些问题,我们系统的中的问题就无法根本性解决。

  1.   Glibc在什么情况下不会将内存归还给操作系统?
  2.   Glibc的内存管理方式有哪些约束?适合什么样的内存分配场景?
  3. 我们的系统中的内存管理方式是与Glibc的内存管理的约束相悖的?
  4.   Glibc是如何管理内存的?

带着这些问题,决定对Glibcptmalloc2源代码进行一番研究,希望能找到这些问题的答案,并解决我们系统中遇到的问题。我研究的对象是当前最新版的glibc-2.12.1中的内存管理的相关代码。

你可能感兴趣的:(C++,c,项目管理,NoSQL,C#)