堆的初始化–源码分析

堆的初始化

堆的初始化是用户在第一次申请内存执行malloc_consolidate 再执行malloc_init_state实现的。

malloc_init_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
#define INTERNAL_SIZE_T size_t
#define SIZE_SZ (sizeof(INTERNAL_SIZE_T))
//就是size_t的长度
typedef struct malloc_state *mstate; //mstate就是malloc_sate 指针
static struct malloc_state main_arena; //main_arena在这里声明的
typedef struct malloc_chunk* mbinptr;
#define bin_at(m, i) ((mbinptr)((char*)&((m)->bins[(i)<<1]) - (SIZE_SZ<<1))) /* 寻址 -- 注意 bin_at(0) 不存在 */

static void malloc_init_state(mstate av) {
int i;
mbinptr bin;

/* 为普通 bin 建立循环链接 */
for (i = 1; i < NBINS; ++i) {//BINS为 128
bin = bin_at(av, i);
bin->fd = bin->bk = bin;
}

#if MORECORE_CONTIGUOUS
if (av != &main_arena)
#endif
set_noncontiguous(av);
//((M)->max_fast |= (using int)2U)
if (av == &main_arena) set_max_fast(DEFAULT_MXFAST);
// 设置 flags 标记目前没有fast chunk
av->flags |= FASTCHUNKS_BIT;
// 就是 unsorted bin
av->top = initial_top(av);
}

malloc_consolidate

由于fastbin中的chunk永远不会释放,导致相邻的free chunk 无法与之合并,从而造成了大量的内存碎片,malloc_consolidate()函数最主要的功能就是来解决这个问题。在达到某些条件时,glibc就会调用该函数将fastbin中的chunk取出来,与相邻的freechunk 合并后放入unsorted bin ,或者与top chunk 合并后形成新的top chunk。

该函数有两个功能:

  • 若fastbin未初始化,即global_max_fast为0,那就初始化malloc_state
  • 如果已经初始化的话,那就合并fastbin中的chunk。
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
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#if __STD_C
static void malloc_consolidate(mstate av)
#else
static void malloc_consolidate(av) mstate av;
#endif
{
mfastbinptr* fb; /* current fastbin being consolidated */
mfastbinptr* maxfb; /* last fastbin (for loop control) */
mchunkptr p; /* current chunk being consolidated */
mchunkptr nextp; /* next chunk to consolidate */
mchunkptr unsorted_bin; /* bin header */
mchunkptr first_unsorted; /* chunk to link to */

/* These have same use as in free() */
mchunkptr nextchunk;
INTERNAL_SIZE_T size;
INTERNAL_SIZE_T nextsize;
INTERNAL_SIZE_T prevsize;
int nextinuse;
mchunkptr bck;
mchunkptr fwd;

/*
If max_fast is 0, we know that av hasn't
yet been initialized, in which case do so below
*/

if (av->max_fast != 0) {
clear_fastchunks(av);

unsorted_bin = unsorted_chunks(av);

/*
Remove each chunk from fast bin and consolidate it, placing it
then in unsorted bin. Among other reasons for doing this,
placing in unsorted bin avoids needing to calculate actual bins
until malloc is sure that chunks aren't immediately going to be
reused anyway.
*/

maxfb = &(av->fastbins[fastbin_index(av->max_fast)]);
fb = &(av->fastbins[0]);
do {
if ( (p = *fb) != 0) {
*fb = 0;

do {
check_inuse_chunk(av, p);
nextp = p->fd;

/* Slightly streamlined version of consolidation code in free() */
size = p->size & ~(PREV_INUSE|NON_MAIN_ARENA);
nextchunk = chunk_at_offset(p, size);
nextsize = chunksize(nextchunk);

if (!prev_inuse(p)) {
prevsize = p->prev_size;
size += prevsize;
p = chunk_at_offset(p, -((long) prevsize));
unlink(p, bck, fwd);
}

if (nextchunk != av->top) {
nextinuse = inuse_bit_at_offset(nextchunk, nextsize);

if (!nextinuse) {
size += nextsize;
unlink(nextchunk, bck, fwd);
} else
clear_inuse_bit_at_offset(nextchunk, 0);

first_unsorted = unsorted_bin->fd;
unsorted_bin->fd = p;
first_unsorted->bk = p;

set_head(p, size | PREV_INUSE);
p->bk = unsorted_bin;
p->fd = first_unsorted;
set_foot(p, size);
}

else {
size += nextsize;
set_head(p, size | PREV_INUSE);
av->top = p;
}

} while ( (p = nextp) != 0);

}
} while (fb++ != maxfb);
}
else {
malloc_init_state(av);
check_malloc_state(av);
}
}

struct malloc_state(arena的实现)

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
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. */
int have_fastchunks;
/* 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 */
unsigned int binmap[BINMAPSIZE];
/* 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;
};
  • glibc中的arena就是用这个结构体表示的
  • 其中包含很多信息:各种bins的信息,top chunk 以及最后一个剩余chunk等
  • bins数组为什么是NBINS*2-2个大小(bins中存储的链表都是双向链表,于是每个链表都需要2个位置来存储fd和bk成员。所以数组要乘以2,但是bins数组的0,1索引不想用来存储bins链表,所以减去2。

成员介绍:

  • last_remainder :当次arena中产生last_remainder时,次成员被初始化,并且指向arena中的last_remainder。last_remainder是切割freechunk后剩下的chunk

  • fastbinY数组:存储的是该arena管理的fastbins

  • bins数组:存储的是该arena管理的smallbins,unsortedbins,largebins

struct _heap_info(堆信息结构体)

  • 我们知道一个线程可以包含多个堆段,这些堆段同属于一个arena来管理。每个堆段的信息就是用这个结构体来表示的
  • 这个不是存储堆块的数据,而是来解释说明这个堆段的
1
2
3
4
5
6
typedef struct _heap_info {
mstate ar_ptr; /* Arena for this heap. */
struct _heap_info *prev; /* Previous heap. */
size_t size; /* Current size in bytes. */
size_t pad; /* Make sure the following data is properly aligned. */
} heap_info;
  • ar_ptr :此堆段归属于哪一个arena管理
  • prev: 前一个堆段