一次艰难的堆探索

堆的认识

初始堆

  • 是虚拟地址空间的的 一块连续的线性区域
  • 提供动态分配的内存,允许程序申请大小未知的内存
  • 在用户与操作系统之间,作为动态内存管理的中间人
  • 响应用户的申请内存请求, 向操作系统申请内存,然后将其返回给用户程序
  • 管理用户所释放的内存,适 时归还给操作系统

  1. 堆区域为Data上边,增长方向为低地址到高地址
  2. shared libraries也是堆区域

申请内存的系统调用

  1. 对于主线程可以用brk、mmap申请栈空间
  2. 对于子线程只能用mmap申请栈空间
  3. brk申请空间是把data上面的heap向上增长,mmap申请空间是在物理内存映射到虚拟地址

堆管理器与用户的交互

arena

内存分配区,可以理解为堆管理器所持有的内存池

操作系统–>堆管理器–>用户

物理内存–>arena–>可用内存

堆管理器与用户的内存交易发生于arena中,可以理解为堆管理器向操作系统批发下来的有有冗余的内存库存

堆的基本单位chunk

用户申请内存的单位,也是堆管理器管理内存的基本单位,malloc()返回指针指向一个chunk的数据区域

1
2
3
4
5
6
7
8
9
10
11
12
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;
};

mallco chunk

  • prve_size :表示前一个已经释放的chunk的大小,当前一个chunk没有释放时,无意义
  • size :表示自身chunk的大小,有三个标志位A、M、P
    • A :chunk 是否属于主线程,1表示不属于,0表示属于
    • M :chunk是否用由mmap系统调用分配,1表示是,0表示不是
    • P :chunk前一个chunk是否在使用,1表示在使用,0表示被释放

free chunk

fastbin free chunk

smallbin free chunk

lagerbin free chunk

bin机制

bin链的保存(struct_ malloc_state结构体)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
typedef struct malloc_chunk* mchunkptr;
typedef struct malloc_chunk *mfastbinptr;
/**
* 全局malloc状态管理
*/
struct malloc_state
{
/* Serialize access. 同步访问互斥锁 */
__libc_lock_define (, mutex);

/* Flags (formerly in max_fast).
* 用于标记当前主分配区的状态
* */
int flags;

/* Set if the fastbin chunks contain recently inserted free blocks. */
/* Note this is a bool but not all targets support atomics on booleans. */
/* 用于标记是否有fastchunk */
int have_fastchunks;

/* Fastbins fast bins。
* fast bins是bins的高速缓冲区,大约有10个定长队列。
* 当用户释放一块不大于max_fast(默认值64)的chunk(一般小内存)的时候,会默认会被放到fast bins上。
* */
mfastbinptr fastbinsY[NFASTBINS];

/* Base of the topmost chunk -- not otherwise kept in a bin */
/* Top chunk :并不是所有的chunk都会被放到bins上。
* top chunk相当于分配区的顶部空闲内存,当bins上都不能满足内存分配要求的时候,就会来top chunk上分配。 */
mchunkptr top;

/* The remainder from the most recent split of a small request */
mchunkptr last_remainder;

/* Normal bins packed as described above
* 常规 bins chunk的链表数组
* 1. unsorted bin:是bins的一个缓冲区。当用户释放的内存大于max_fast或者fast bins合并后的chunk都会进入unsorted bin上
* 2. small bins和large bins。small bins和large bins是真正用来放置chunk双向链表的。每个bin之间相差8个字节,并且通过上面的这个列表,
* 可以快速定位到合适大小的空闲chunk。
* 3. 下标1是unsorted bin,2到63是small bin,64到126是large bin,共126个bin
* */
mchunkptr bins[NBINS * 2 - 2];

/* Bitmap of bins
* 表示bin数组当中某一个下标的bin是否为空,用来在分配的时候加速
* */
unsigned int binmap[BINMAPSIZE];

/* 分配区全局链表:分配区链表,主分配区放头部,新加入的分配区放main_arean.next 位置 Linked list */
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;
};

实际编译后这个结构体就是arena

  • fastbinY数组:大小为10。记录的是fast bin链。

  • bins数组:大小为129。记录的是unsorted bin(1)、small bin(263)、large bin链(64126)。

fast bin

fast bin是ptmalloc为了解决用户频繁的创建空间还能快速响应的结构

表头为物理连接,每个bin链为逻辑连接,用bk指针

fast bin特点

  • 采用LIFO策略,和栈类似,为单链表结构
  • chunk的inuse bit永远是1。应为fast bin会被频繁使用,所以fast bin是不参与合并的,

今后学到补充补充

unsorted bin

small bin

large bin