概述
堆(heap)是指进程虚拟空间中由开发者动态分配的内存区域,它与栈不同,具有地址向上增长的特点,并且由开发者自己分配与释放。开发者一般通过malloc与free管理动态内存,而malloc和free其实是glibc动态内存管理的对外接口。
为了避免用户频繁使用系统调用带来的开销以及尽可能地提高内存使用率,glibc基于系统调用brk和mmap向开发者提供了一套堆内存管理机制。glibc采用的内存分配器(Memory Allocator)是ptmalloc2,其通过预留内存以及将释放的内存块通过分类有效组织起来,以此来提高内存分配速度和内存空间使用率。
1. 堆内存管理基础——mmap 和 brk
mmap和brk是Linux提供的用于动态内存分配的系统调用,也是glibc堆内存管理的基础,因此先了解这两个系统调用是如何使用的,同时需要了解这两个系统调用都是在哪分配的内存。
1.1 进程空间
32位模式下的进程默认内存布局
栈至顶向下扩展,并且栈是有界的。堆至底向上扩展, mmap 映射区域至顶向下扩展, mmap 映射区域和堆相对扩展,直至耗尽虚拟地址空间中的剩余区域,这种结构便于 C 运行时库使用 mmap 映射区域和堆进行内存分配。上图的布局形式是在内核2.6.7 以后才引入的,这是 32 位模式下进程的默认内存布局形式。本文将glibc通过brk和mmap所获取的动态内存统称为堆。
1.2 brk
lang=c
#include <unistd.h>
int brk(void *addr);
void *sbrk(intptr_t increment);
brk系统调用将从bss段边界向上增长,这一部分增长的内存可由开发者使用,brk调用设定了堆的边界,而sbrk是c库函数,其参数为欲申请的的新的堆空间大小。当sbrk的参数increment的大小为0是,返回的是当前堆的边界;而当inicrement的大小为负时,将对堆进行收缩。
1.3 mmap
lang=c
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
mmap系统调用将一个文件或者其它对象映射进内存。这部分内存的地址和长度分别由addr和length指定,prot只这部分内存的保护标志,在glibc中,通常将新分配内存设置为可读可写的标志。而unmap的功能正好相反,将这部分内存归还给内核。
1.4 小结
可见,glibc所管理的内存实际上是由两部分内存组成的,一部分由brk申请,位于进程空间的堆区;而另一部分,则由mmap申请,位于mmap映射区域。那么,什么时候会使用brk申请内存,什么时候会使用mmap申请内存?简单来说,就是主arena会使用sbrk分配内存,子arena会使用mmap申请内存。那么什么是子arena,什么是主arena。简单来说,就是主线程的内存分配区和子线程的内存分配区。下面详细了解以下什么是分配区。
2. 堆内存管理的组织——分配区(Arena),堆(Heap)和内存块(Chunk)
下两张图清楚地显示了Arena,堆和chunk在内存空间的关系,可以在了解完三者后,再回过头来看这张图。
2.1 多线程与Arena
(1)ptmalloc的多线程支持
Linux早期版本采用dlmalloc作为它的默认分配器,但是dlmalloc并不对多线程并不友好,这是因为在dlmalloc中,线程之间共享一个堆,因此临界区的竞争将会降低内存分配的效率。而ptmalloc则友好地支持了多线程环境,其通过为线程提供各自的堆,从而各个线程可以并发地申请内存,发现没,ptmalloc就是per thread malloc的缩写。管理堆的数据结构被称为Arena。
(2)Arena和线程的对应关系
可惜的是,Arena的数量是有限的,这和CPU核数相关,因此,不一定所有的线程都独享自己的Arena,当线程数量大于Arena的数量限制时,就会出现线程之间共享Arena的情况了。主线程拥有自己的arena,该arena的堆区在glibc的代码中,其为一个静态全局变量!!main_arena!!,而其它的子线程,则根据需要动态分配得到子Arena。
(3)Arena和堆的对应关系
主Arena对应的堆通过brk申请,而子Arena对应的堆区则通过mmap申请。因此主Arena只有一个堆,而子Arena可以对应多个堆,当原来的堆不够用时,就会申请新的堆,然后和原来的堆链为一个链表。
(4)Arena的数据结构
struct malloc_state
{
/* Serialize access. */
mutex_t mutex;//互斥量,用于多线程共享一个Arena
/* Flags (formerly in max_fast). */
int flags;
#if THREAD_STATS
/* Statistics for locking. Only used if THREAD_STATS is defined. */
long stat_lock_direct, stat_lock_loop, stat_l