1 堆管理器
1.1 堆概述
什么是堆?
-
是虚拟地址空间的一块连续的线性区域
-
提供动态分配的内存,允许程序申请大小未知的内存
-
在用户与操作系统之间,作为动态内存管理的中间人
-
响应用户的申请内存请求, 向操作系统申请内存,然后将其返回给用户程序
-
管理用户所释放的内存,适 时归还给操作系统
各种堆管理器
堆管理器并非由操作系统实现,而是由libc.so.6链接库实现。 封装了一些系统调用,为用户提供方便的动态内存分配接口的同时,力求高效地管理由系统调用申请来的内存。 -
dlmalloc – General purpose allocator
-
ptmalloc2 – glibc (敲黑板)
-
jemalloc – FreeBSD and Firefox
-
tcmalloc – Google libumem – Solaris
1.2 arena
1.3 chunk
用户申请内存的单位,也是堆管理器管理内存的基本单位 malloc()返回的指针指向一个chunk的数据区域。
chunk 的具体实现
struct malloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */
INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
struct malloc_chunk* bk_nextsize;
};
chunk 的分类
按状态
- malloced
- free
按大小 - fast
- small
- targe
- tcache
按特定功能 - top chunk
- last remainder chunk
malloced chunk
已被分配且填写了相应数据的chunk
free chunk
被释放掉的malloced chunk成为free chunk
top chunk
arena中从未被使用过的内存区域
last remainder chunk
malloc分割原chunk后剩余的部分
Chunk 的微观结构
1.4 bin
管理 arena 中空闲 chunk 的结构,以数组的形式存在,数组元素为相应大小的 chunk 链表的链表头,存在于 arena 的 malloc_state 中
- unsorted bin
- fast bins
- small bins
- large bins
- (tcache)
prev_size
若前一个物理相邻的chunk是free chunk,则表示其大小。否则用于存储前一个chunk的数据
size
占据一字长的低3bits以后的地址,用于表示当前chunk的大小(整个chunk的大小,包括chunk头)
A flag
NON_MAIN_ARENA,记录当前 chunk是否不属于主线程,1 表示不属于,0 表示属于。
M flag
IS_MAPPED,记录当前 chunk 是否是由 mmap 分配的。
P flag
PREV_INUSE,记录前一个 chunk 块是否被分配。一般来说,堆中第一个被分配的内存块的 size 字段的 P 位都会被设置为 1,以便于防止访问前面的非法内存。当一个 chunk 的 size 的 P 位为 0 时,我们能通过 prev_size 字段来获取上一个 chunk 的大小以及地址。这也方便进行空闲 chunk 之间的合并。
fd pointer
在bin中指向下一个(非物理相邻)空闲的 chunk.
bk pointer
在bin中指向上一个(非物理相邻)空闲的 chunk
fd_nextsize
在large bin中指向前一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针
bk_nextsize
在large bin中指向后一个与当前 chunk 大小不同的第一个空闲块,不包含 bin 的头指针
一般空闲的 large chunk 在 fd 的遍历顺序中,按照由大到小的顺序排列。这样做可以避免在寻找合适 chunk 时挨个遍历
main arena 的 malloc_state 并不是 heap segment 的一部分,而是一个全局变量,存储在 libc.so 的数据段
struct malloc_state {
/* Serialize access. */
__libc_lock_define(, mutex); /* Flags (formerly in max_fast). */
int flags; /* Fastbins */
mfastbinptr fastbinsY[ NFASTBINS ]; /* Base of the topmost chunk -- not otherwise kept in a bin */
mchunkptr top; /* The remainder from the most recent split of a small request */
mchunkptr last_remainder; /* Normal bins packed as described above */
mchunkptr bins[ NBINS * 2 - 2 ]; /* Bitmap of bins, help to speed up the process of determinating if a given bin is definitely empty.*/
unsigned int binmap[ BINMAPSIZE ]; /* Linked list, points to the next arena */
struct malloc_state *next; /* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_state *next_free; /* Number of threads attached to this arena. 0 if the arena is on
the free list. Access to this field is serialized by
free_list_lock in arena.c. */
INTERNAL_SIZE_T attached_threads; /* Memory allocated from the system in this arena. */
INTERNAL_SIZE_T system_mem;
INTERNAL_SIZE_T max_system_mem;
};
2 堆分配策略
malloc
- 它根据用户申请的内存块大小以及相应大小 chunk 通常使用的频度(fastbin chunk, small chunk, large chunk),依次实现了不同的分配方法。
- 它由小到大依次检查不同的 bin 中是否有相应的空闲块可以满足用户请求的内存。
- 当所有的空闲 chunk 都无法满足时,它会考虑 top chunk。
- 当 top chunk 也无法满足时,堆分配器才会进行内存块申请。
free
- 它将用户暂且不用的chunk回收给堆管理器,适当的时候还会归还给操作系统。
- 它依据chunk大小来优先试图将free chunk链入tcache或者是fast bin。不满足则链入usorted bin中。
- 在条件满足时free函数遍历usorted bin并将其中的物理相邻的free chunk合并,将相应大小的chunk分类放入small bin或large bin中。
- 除了tcache chunk与fast bin chunk,其它chunk在free时会与其物理相邻的free chunk合并