编辑: kr9梯 | 2016-06-22 |
1 目录 1. 问题.3 2. 基础知识.4 2.1 X86 平台 Linux 进程内存布局.4 2.1.1
32 位模式下进程内存经典布局.4 2.1.2
32 位模式下进程默认内存布局.5 2.1.3
64 位模式下进程内存布局.5 2.2 操作系统内存分配的相关函数.6 2.2.1 Heap 操作相关函数
6 2.2.2 Mmap 映射区域操作相关函数
7 3. 概述.8 3.1 内存管理一般性描述.8 3.1.1 内存管理的方法.8 3.1.2 内存管理器的设计目标.10 3.1.3 常见 C 内存管理程序.12 3.2 Ptmalloc 内存管理概述.13 3.2.1 简介.13 3.2.2 内存管理的设计假设.14 3.2.3 内存管理数据结构概述.14 3.2.4 内存分配概述.19 3.2.5 内存回收概述.21 3.2.6 配置选项概述.22 3.2.7 使用注意事项.23 4. 问题分析及解决.24 5. 源代码分析.26 5.1 边界标记法.26 5.2 分箱式内存管理.34 5.2.1 Small bins
34 5.2.2 Large bins
35 5.2.3 Unsorted bin.40 5.2.4 Fast bins.42 5.3 核心结构体分析.44 5.3.1 malloc_state.44 5.3.2 Malloc_par
47 5.3.3 分配区的初始化.49 5.4 配置选项.51 5.5 Ptmalloc 的初始化.53 5.5.1 Ptmalloc 未初始化时分配/释放内存
53 5.5.2 ptmalloc_init()函数.55 5.5.3 ptmalloc_lock_all(),ptmalloc_unlock_all(),ptmalloc_unlock_all2(60 5.6 多分配区支持.65 5.6.1 Heap_info.65 5.6.2 获取分配区.66
2 5.6.3 Arena_get2(68 5.6.4 _int_new_arena(70 5.6.5 New_heap(72 5.6.6 get_free_list()和reused_arena(75 5.6.7 grow_heap(),shrink_heap(),delete_heap(),heap_trim(77 5.7 内存分配 malloc.82 5.7.1 public_mALLOc(82 5.7.2 _int_malloc(83 5.8 内存释放 free.116 5.8.1 Public_fREe(116 5.8.2 _int_free(118 5.8.3 sYSTRIm()和munmap_chunk(126
3 1.问题 项目组正在研发的一个类似数据库的 NoSql 系统,遇到了 Glibc 的内存暴增问题.现象 如下:在我们的 NoSql 系统中实现了一个简单的内存管理模块,在高压力高并发环境下长时 间运行,当内存管理模块的内存释放给 C 运行时库以后,C 运行时库并没有立即把内存归还 给操作系统,比如内存管理模块占用的内存为 10GB,释放内存以后,通过 TOP 命令或者 /proc/pid/status 查看进程占用的内存有时仍然为 10G,有时为 5G,有时为 3G,etc,内存释 放的行为不确定. 我们的 NoSql 系统中的内存管理方式比较简单,使用全局的定长内存池,内存管理模块 每次分配/释放 2MB 内存, 然后分成 64KB 为单位的一个个小内存块用 hash 加链表的方式进 行管理.如果申请的内存小于等于 64KB 时,直接从内存池的空闲链表中获取一个内存块, 内存释放时归还空闲链表;
如果申请的内存大于 64KB, 直接通过 C 运行时库的 malloc 和free 获取.某些数据结构涉及到很多小对象的管理,比如 Hash 表,B-Tree,这些数据结构从全 局内存池获取内存后再根据数据结构的特点进行组织.为了提高内存申请/释放的效率,减 少锁冲突,为每一个线程单独保留 8MB 的内存块,每个线程优先从线程专属的 8MB 内存块 获取内存,专属内存不足时才从全局的内存池获取. 系统中使用的网络库有独立的内存管理方式,并不从全局内存池中分配内存,该网络库 在处理网络请求时也是按 2M 内存为单位向 C 运行时库申请内存,一次请求完成以后,释放 分配的内存到 C 运行时库. 在弄清楚了系统的内存分配位置以后,对整个系统进行了内存泄露的排查,在解决了数 个内存泄露的潜在问题以后, 发现系统在高压力高并发环境下长时间运行仍然会发生内存暴 增的现象,最终进程因 OOM 被操作系统杀掉. 为了便于跟踪分析问题,在全局的内存池中加入对每个子模块的内存统计功能:每个子 模块申请内存时都将子模块编号传给全局的内存池, 全局的内存池进行统计. 复现问题后发 现全局的内存池的统计结果符合预期, 同样对网络模块也做了类似的内存使用统计, 仍然符 合预期.由于内存管理不外乎三个层面,用户管理层,C 运行时库层,操作系统层,在操作 系统层发现进程的内存暴增, 同时又确认了用户管理层没有内存泄露, 因此怀疑是 C 运行时 库的问题,也就是 Glibc 的内存管理方式导致了进程的内存暴增. 问题范围缩小了,但有如下的问题还没有搞清楚,搞不清楚这些问题,我们系统的中的 问题就无法根本性解决. 1. Glibc 在什么情况下不会将内存归还给操作系统? 2. Glibc 的内存管理方式有哪些约束?适合什么样的内存分配场景? 3. 我们的系统中的内存管理方式是与 Glibc 的内存管理的约束相悖的? 4. Glibc 是如何管理内存的? 带着这些问题,决定对 Glibc 的ptmalloc2 源代码进行一番研究,希望能找到这些问题的 答案,并解决我们系统中遇到的问题.我研究的对象是当前最新版的 glibc-2.12.1 中的内存 管理的相关代码.