typedefstruct _heap_info { mstate ar_ptr; /* Arena for this heap. */ struct _heap_info *prev;/* Previous heap. */ size_t size; /* Current size in bytes. */ size_t mprotect_size; /* Size in bytes that has been mprotected PROT_READ|PROT_WRITE. */ /* Make sure the following data is properly aligned, particularly that sizeof (heap_info) + 2 * SIZE_SZ is a multiple of MALLOC_ALIGNMENT. */ char pad[-6 * SIZE_SZ & MALLOC_ALIGN_MASK]; } heap_info;
structmalloc_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 */ unsignedint binmap[BINMAPSIZE]; /* Linked list */ structmalloc_state *next; /* Linked list for free arenas. Access to this field is serialized by free_list_lock in arena.c. */ structmalloc_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; };
/* This struct declaration is misleading (but accurate and necessary). It declares a "view" into memory allowing access to necessary fields at known offsets from a given base. See explanation below. */ structmalloc_chunk {
INTERNAL_SIZE_T prev_size; /* Size of previous chunk (if free). */ INTERNAL_SIZE_T size; /* Size in bytes, including overhead. */
structmalloc_chunk* fd;/* double links -- used only if free. */ structmalloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */ structmalloc_chunk* fd_nextsize;/* double links -- used only if free. */ structmalloc_chunk* bk_nextsize; };
在实践中,程序申请和释放的堆块都比较小,所以glibc对这类bin采用单链表结构,并采用先进后出的分配策略。为了加快速度,fast bin不会对chunk进行合并操作,所以下一个chunk的PRV_INUSE始终标记为1,使其处于使用状态。同一个fast bin 里的chunk 大小相同,并在fastbinsY数组里按照从小到大的顺序排序,序号为0的fast bin中容纳的chunk大小为4*SIZE_SZ字节,随着序号的增加,所容纳的chunk递增2*SIZE_SZ字节。
unsorted bin
一定大小的chunk被释放时,在进入small bin和large bin 之前,会先加入unsorted bin 。在实践中,一个被释放的chunk常常很快就会被重新使用,所以先将其加入unsorted bin ,可以加快分配的速度。unsorted bin使用双链表结构,并采用先进后出的分配策略。与fastbinsY不同,unsorted bin 中的chunk大小可能是不同的,并且由于是双链表结构,一个bin会占用bins的两个元素。
small bin
同一个small bin里,chunk 的大小相同,采用双链表结构,使用频率介于fast bin 和large bin 之间。small bin 在bins里面居第2到第63位,共62个。根据排序,每个small bin的大小为2*SIZE_SZ*idx(idx表示bins数组的下标)。在64位系统下,最小的small bin为32字节,最大的为1008字节。由于small bin和fast bin有重合的部分,所以这些chunk在某些情况下会被加入到small bin中。
large bin
large bin 在bins里面居64位到126位,共63个,被分成了6组,每组所能容纳的chunk 按顺序排列成等差数列。
组
数量
公差
1
32
64B
2
16
512B
3
8
4096B
4
4
32768B
5
2
262144B
6
1
不限制
large bin 也是采用的双链表结构,里面的chunk从头结点的fd指针开始,按大小顺序进行排序。为了加快检索速度,fd_nextsize 和bk_nextsize指针用于指向第一个与自己大小不同的chunk,所以也只有在加入了大小不同的chunk时,这两个指针才会被修改。
可能有的人会疑惑,0x18的user data 加上头部0x10就已经是0x28了,为什么返回的chunk却是0x20。这是因为如果当前chunk在使用中,下一个chunk的pre_inuse 成员就会属于当前chunk,所以就多出了0x8的使用空间。考虑到这一点,当req在0x9到0x18之间时,对应的chunk大小为0x10,当req在0x19到0x28之间时,对应的chunk大小为0x20。
/* Set size at head, without disturbing its use bit */ #define set_head_size(p, s) ((p)->mchunk_size = (((p)->mchunk_size & SIZE_BITS) | (s)))
/* Set size/use field */ #define set_head(p, s) ((p)->mchunk_size = (s))
/* Set size at footer (only when chunk is not in use) */ #define set_foot(p, s) (((mchunkptr) ((char *) (p) + (s)))->mchunk_prev_size = (s)) /* Get size, ignoring use bits */ #define chunksize(p) (chunksize_nomask (p) & ~(SIZE_BITS))
/* Fastbins An array of lists holding recently freed small chunks. Fastbins are not doubly linked. It is faster to single-link them, and since chunks are never removed from the middles of these lists, double linking is not necessary. Also, unlike regular bins, they are not even processed in FIFO order (they use faster LIFO) since ordering doesn't much matter in the transient contexts in which fastbins are normally used. Chunks in fastbins keep their inuse bit set, so they cannot be consolidated with other free chunks. malloc_consolidate releases all chunks in fastbins and consolidates them with other free chunks. */ typedefstructmalloc_chunk *mfastbinptr; #define fastbin(ar_ptr, idx) ((ar_ptr)->fastbinsY[idx]) /* offset 2 to use otherwise unindexable first 2 bins */ #define fastbin_index(sz) \ ((((unsigned int) (sz)) >> (SIZE_SZ == 8 ? 4 : 3)) - 2) /* The maximum fastbin request size we support */ #define MAX_FAST_SIZE (80 * SIZE_SZ / 4) #define NFASTBINS (fastbin_index (request2size (MAX_FAST_SIZE)) + 1)
fastbin中的chunk一遍不会与其他chunk合并。但如果合并之后的chunk大于FASTBIN_CONSOLIDATION_THRESHOLD,就会触发malloc_consolidate()函数,将fastbin中的chunk与其他free chunk 合并,然后移动到unsorted bin 中。
1
#define FASTBIN_CONSOLIDATION_THRESHOLD (65536UL)
fast bin 中最大的chunk是由global_max_fast 决定的,这个值一般在堆初始化的时候设置,当然在运行时也可以设置的。
bins[0]和bins[1]是unsorted bin 的fd和bk指针,binat(1)返回的应该是unsorted bin 的头指针,但实际上其指向的是bins[0]地址减去offsetof (struct malloc_chunk, fd))的位置,这样使用头结点指证b时,b->fd或者b->bk能够正确访问,同时prev_size和size对于头结点没有意义,所以就被省略了。对于binat(64)及其之后的large bin来说,因为头结点的size成员没有意义,所以其fd_nextsize和bk_nextsize也是没有意义的,也可以省略。
/* ------------------------- malloc_consolidate ------------------------- malloc_consolidate is a specialized version of free() that tears down chunks held in fastbins. Free itself cannot be used for this purpose since, among other things, it might place chunks back onto fastbins. So, instead, we need to use a minor variant of the same code. */ staticvoidmalloc_consolidate(mstate av) { 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; atomic_store_relaxed (&av->have_fastchunks, false); 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 = &fastbin (av, NFASTBINS - 1); fb = &fastbin (av, 0); do { p = atomic_exchange_acq (fb, NULL); if (p != 0) { do { { unsignedint idx = fastbin_index (chunksize (p)); if ((&fastbin (av, idx)) != fb) malloc_printerr ("malloc_consolidate(): invalid chunk size"); } check_inuse_chunk(av, p); nextp = p->fd; /* Slightly streamlined version of consolidation code in free() */ size = chunksize (p); nextchunk = chunk_at_offset(p, size); nextsize = chunksize(nextchunk); if (!prev_inuse(p)) { prevsize = prev_size (p); size += prevsize; p = chunk_at_offset(p, -((long) prevsize)); if (__glibc_unlikely (chunksize(p) != prevsize)) malloc_printerr ("corrupted size vs. prev_size in fastbins"); unlink_chunk (av, p); } if (nextchunk != av->top) { nextinuse = inuse_bit_at_offset(nextchunk, nextsize); if (!nextinuse) { size += nextsize; unlink_chunk (av, nextchunk); } else clear_inuse_bit_at_offset(nextchunk, 0); first_unsorted = unsorted_bin->fd; unsorted_bin->fd = p; first_unsorted->bk = p; if (!in_smallbin_range (size)) { p->fd_nextsize = NULL; p->bk_nextsize = NULL; } 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); }
void * __libc_malloc (size_t bytes) { mstate ar_ptr; void *victim; _Static_assert (PTRDIFF_MAX <= SIZE_MAX / 2, "PTRDIFF_MAX is not more than half of SIZE_MAX"); //检查是否内存分配钩子,如果有,调用钩子并返回. void *(*hook) (size_t, constvoid *) = atomic_forced_read (__malloc_hook); if (__builtin_expect (hook != NULL, 0)) return (*hook)(bytes, RETURN_ADDRESS (0));
接着会寻找一个arena来试图分配内存。
1
arena_get(ar_ptr, bytes);
然后调用_int_malloc函数去申请对应的内存。
1
victim = _int_malloc (ar_ptr, bytes);
如果分配失败的话,ptmalloc会尝试再去寻找一个可用的arena,并分配内存。
1 2 3 4 5 6 7 8
/* Retry with another arena only if we were able to find a usable arena before. */ if (!victim && ar_ptr != NULL) { LIBC_PROBE (memory_malloc_retry, 1, bytes); ar_ptr = arena_get_retry (ar_ptr, bytes); victim = _int_malloc (ar_ptr, bytes); }
如果申请到了arena,那么在退出之前还得解锁。
1 2
if (ar_ptr != NULL) __libc_lock_unlock (ar_ptr->mutex);
staticvoid *_int_malloc(mstate av, size_t bytes) { INTERNAL_SIZE_T nb; /* normalized request size */ unsignedint idx; /* associated bin index */ mbinptr bin; /* associated bin */
mchunkptr victim; /* inspected/selected chunk */ INTERNAL_SIZE_T size; /* its size */ int victim_index; /* its bin index */
mchunkptr remainder; /* remainder from a split */ unsignedlong remainder_size; /* its size */
unsignedint block; /* bit map traverser */ unsignedint bit; /* bit map traverser */ unsignedintmap; /* current word of binmap */
mchunkptr fwd; /* misc temp for linking */ mchunkptr bck; /* misc temp for linking */
constchar *errstr = NULL;
/* Convert request size to internal form by adding SIZE_SZ bytes overhead plus possibly more to obtain necessary alignment and/or to obtain a size of at least MINSIZE, the smallest allocatable size. Also, checked_request2size traps (returning 0) request sizes that are so large that they wrap around zero when padded and aligned. */
checked_request2size(bytes, nb);
如果没有合适的arena,就调用sysmalloc(),用mmap()分配chunk并返回。
1 2 3 4 5 6 7
/* There are no usable arenas. Fall back to sysmalloc to get a chunk from mmap. */ if (__glibc_unlikely(av == NULL)) { void *p = sysmalloc(nb, av); if (p != NULL) alloc_perturb(p, bytes); return p; }
/* If the size qualifies as a fastbin, first check corresponding bin. This code is safe to execute even if av is not yet initialized, so we can try it without checking, which saves some time on this fast path. */
/* If a small request, check regular bin. Since these "smallbins" hold one size each, no searching within bins is necessary. (For a large request, we need to wait until unsorted chunks are processed to find best fit. But for small ones, fits are exact anyway, so we can check now, which is faster.) */
if (in_smallbin_range(nb)) { // 获取 small bin 的索引 idx = smallbin_index(nb); // 获取对应 small bin 中的 chunk 指针 bin = bin_at(av, idx); // 先执行 victim = last(bin),获取 small bin 的最后一个 chunk // 如果 victim = bin ,那说明该 bin 为空。 // 如果不相等,那么会有两种情况 if ((victim = last(bin)) != bin) { // 第一种情况,small bin 还没有初始化。 if (victim == 0) /* initialization check */ // 执行初始化,将 fast bins 中的 chunk 进行合并 malloc_consolidate(av); // 第二种情况,small bin 中存在空闲的 chunk else { // 获取 small bin 中倒数第二个 chunk 。 bck = victim->bk; // 检查 bck->fd 是不是 victim,防止伪造 if (__glibc_unlikely(bck->fd != victim)) { errstr = "malloc(): smallbin double linked list corrupted"; goto errout; } // 设置 victim 对应的 inuse 位 set_inuse_bit_at_offset(victim, nb); // 修改 small bin 链表,将 small bin 的最后一个 chunk 取出来 bin->bk = bck; bck->fd = bin; // 如果不是 main_arena,设置对应的标志 if (av != &main_arena) set_non_main_arena(victim); // 细致的检查,非调试状态没有作用 check_malloced_chunk(av, victim, nb); // 将申请到的 chunk 转化为对应的 mem 状态 void *p = chunk2mem(victim); // 如果设置了 perturb_type , 则将获取到的chunk初始化为 perturb_type ^ 0xff alloc_perturb(p, bytes); return p; } } }
/* If this is a large request, consolidate fastbins before continuing. While it might look excessive to kill all fastbins before even seeing if there is space available, this avoids fragmentation problems normally associated with fastbins. Also, in practice, programs tend to have runs of either small or large requests, but less often mixtures, so consolidation is not invoked all that often in most programs. And the programs that it is called frequently in otherwise tend to fragment. */ /*到这里还不能满足,就调用malloc_consolidate()整理fastbins,或许会有合适的chunk*/ else { // 获取large bin的下标。 idx = largebin_index(nb); // 如果存在fastbin的话,会处理 fastbin if (have_fastchunks(av)) malloc_consolidate(av); }
大循环-遍历unsorted bin
接下来,函数进入一个大的外层for循环,包含了_int_malloc()函数之后的所有过程。紧接着是内层第一个while循环,它会遍历unsorted bin中的每一个chunk,如果大小正好合适,就将其取出,否则就将其放入small bin或者large bin 。这是唯一的将chunk放进small bin或者large bin的过程。
如果程序执行到了这里,那么说明与chunk大小正好一致的bin(fast bin ,small bin)中没有chunk可以直接满足需求,但是large chunk则是在这个大循环中处理。
在接下来的这个循环中,主要做了以下的操作
按照FIFO的方式逐个将unsorted bin中的chunk取出来
如果是small request ,则考虑是不是恰好满足,是的话,直接返回。
如果不是得话,放到对应的bin中。
尝试从large bin中分配用户所需的内存
该部分是个大循环,这是为了尝试重新分配small bin chunk ,这是因为我们虽然会首先使用large bin,top chunk来尝试满足用户的请求,但是如果没有满足的话,由于我们在上面没有分配成功small bin,我们并没有对fast bin中的chunk进行合并,所以这里会进行fast bin chunk的合并,进而使用一个大循环来尝试再次分配small bin chunk。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
/* Process recently freed or remaindered chunks, taking one only if it is exact fit, or, if this a small request, the chunk is remainder from the most recent non-exact fit. Place other traversed chunks in bins. Note that this step is the only place in any routine where chunks are placed in bins. The outer loop here is needed because we might not realize until near the end of malloc that we should have consolidated, so must do so and retry. This happens at most once, and only when we would otherwise need to expand memory to service a "small" request. */
for (;;) { int iters = 0;
unsorted bin遍历
先考虑unsorted bin ,再考虑last remainder ,但是对于small bin chunk的请求会有所例外。
注意unsorted bin的遍历顺序为bk。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// 如果 unsorted bin 不为空 // First In First Out while ((victim = unsorted_chunks(av)->bk) != unsorted_chunks(av)) { // victim 为 unsorted bin 的最后一个 chunk // bck 为 unsorted bin 的倒数第二个 chunk bck = victim->bk; // 判断得到的 chunk 是否满足要求,不能过小,也不能过大 // 一般 system_mem 的大小为132K if (__builtin_expect(chunksize_nomask(victim) <= 2 * SIZE_SZ, 0) || __builtin_expect(chunksize_nomask(victim) > av->system_mem, 0)) malloc_printerr(check_action, "malloc(): memory corruption", chunk2mem(victim), av); // 得到victim对应的chunk大小。 size = chunksize(victim);
small request
如果用户的请求为small bin chunk ,那么我们首先考虑last remainder ,如果last remainder 是unsorted bin中的唯一一块的话,并且满足拆分条件时,就将其拆分。
/* If a small request, try to use last remainder if it is the only chunk in unsorted bin. This helps promote locality for runs of consecutive small requests. This is the only exception to best-fit, and applies only when there is no exact fit for a small chunk. */
/* Take now instead of binning if exact fit */ if (size == nb) { set_inuse_bit_at_offset(victim, size); if (av != &main_arena) set_non_main_arena(victim); check_malloced_chunk(av, victim, nb); void *p = chunk2mem(victim); alloc_perturb(p, bytes); return p; }
/* If a large request, scan through the chunks of current bin in sorted order to find smallest that fits. Use the skip list for this. */ if (!in_smallbin_range(nb)) { bin = bin_at(av, idx); /* skip scan if empty or largest chunk is too small */ // 如果对应的 bin 为空或者其中的chunk最大的也很小,那就跳过 // first(bin)=bin->fd 表示当前链表中最大的chunk if ((victim = first(bin)) != bin && (unsignedlong) chunksize_nomask(victim) >= (unsignedlong) (nb)) { // 反向遍历链表,直到找到第一个不小于所需chunk大小的chunk victim = victim->bk_nextsize; while (((unsignedlong) (size = chunksize(victim)) < (unsignedlong) (nb))) victim = victim->bk_nextsize;
/* Avoid removing the first entry for a size so that the skip list does not have to be rerouted. */ // 如果最终取到的chunk不是该bin中的最后一个chunk,并且该chunk与其前面的chunk // 的大小相同,那么我们就取其前面的chunk,这样可以避免调整bk_nextsize,fd_nextsize // 链表。因为大小相同的chunk只有一个会被串在nextsize链上。 if (victim != last(bin) && chunksize_nomask(victim) == chunksize_nomask(victim->fd)) victim = victim->fd; // 计算分配后剩余的大小 remainder_size = size - nb; // 进行unlink unlink(av, victim, bck, fwd);
/* Exhaust */ // 剩下的大小不足以当做一个块 // 很好奇接下来会怎么办? if (remainder_size < MINSIZE) { set_inuse_bit_at_offset(victim, size); if (av != &main_arena) set_non_main_arena(victim); } /* Split */ // 剩下的大小还可以作为一个chunk,进行分割。 else { // 获取剩下那部分chunk的指针,称为remainder remainder = chunk_at_offset(victim, nb); /* We cannot assume the unsorted list is empty and therefore have to perform a complete insert here. */ // 插入unsorted bin中 bck = unsorted_chunks(av); fwd = bck->fd; // 判断 unsorted bin 是否被破坏。 if (__glibc_unlikely(fwd->bk != bck)) { errstr = "malloc(): corrupted unsorted chunks"; goto errout; } remainder->bk = bck; remainder->fd = fwd; bck->fd = remainder; fwd->bk = remainder; // 如果不处于small bin范围内,就设置对应的字段 if (!in_smallbin_range(remainder_size)) { remainder->fd_nextsize = NULL; remainder->bk_nextsize = NULL; } // 设置分配的chunk的标记 set_head(victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0));
/* Search for a chunk by scanning bins, starting with next largest bin. This search is strictly by best-fit; i.e., the smallest (with ties going to approximately the least recently used) chunk that fits is selected. The bitmap avoids needing to check that most blocks are nonempty. The particular case of skipping all bins during warm-up phases when no chunks have been returned yet is faster than it might look. */
/* Skip rest of block if there are no more set bits in this block. */ // 如果bit>map,则表示该 map 中没有比当前所需要chunk大的空闲块 // 如果bit为0,那么说明,上面idx2bit带入的参数为0。 if (bit > map || bit == 0) { do { // 寻找下一个block,直到其对应的map不为0。 // 如果已经不存在的话,那就只能使用top chunk了 if (++block >= BINMAPSIZE) /* out of bins */ goto use_top; } while ((map = av->binmap[ block ]) == 0); // 获取其对应的bin,因为该map中的chunk大小都比所需的chunk大,而且 // map本身不为0,所以必然存在满足需求的chunk。 bin = bin_at(av, (block << BINMAPSHIFT)); bit = 1; } /* Advance to bin with set bit. There must be one. */ // 从当前map的最小的bin一直找,直到找到合适的bin。 // 这里是一定存在的 while ((bit & map) == 0) { bin = next_bin(bin); bit <<= 1; assert(bit != 0); }
/* Inspect the bin. It is likely to be non-empty */ // 获取对应的bin victim = last(bin);
/* If a false alarm (empty bin), clear the bit. */ // 如果victim=bin,那么我们就将map对应的位清0,然后获取下一个bin // 这种情况发生的概率应该很小。 if (victim == bin) { av->binmap[ block ] = map &= ~bit; /* Write through */ bin = next_bin(bin); bit <<= 1; } else { // 获取对应victim的大小 size = chunksize(victim);
/* We know the first chunk in this bin is big enough to use. */ assert((unsignedlong) (size) >= (unsignedlong) (nb)); // 计算分割后剩余的大小 remainder_size = size - nb;
/* unlink */ unlink(av, victim, bck, fwd);
将取出来的victim进行切分并把remainder 加入unsorted bin ,如果victim
use_top: /* If large enough, split off the chunk bordering the end of memory (held in av->top). Note that this is in accord with the best-fit search rule. In effect, av->top is treated as larger (and thus less well fitting) than any other available chunk since it can be extended to be as large as necessary (up to system limitations). We require that av->top always exists (i.e., has size >= MINSIZE) after initialization, so if it would otherwise be exhausted by current request, it is replenished. (The main reason for ensuring it exists is that we may need MINSIZE space to put in fenceposts in sysmalloc.) */ // 获取当前的top chunk,并计算其对应的大小 victim = av->top; size = chunksize(victim); // 如果分割之后,top chunk 大小仍然满足 chunk 的最小大小,那么就可以直接进行分割。 if ((unsignedlong) (size) >= (unsignedlong) (nb + MINSIZE)) { remainder_size = size - nb; remainder = chunk_at_offset(victim, nb); av->top = remainder; // 这里设置 PREV_INUSE 是因为 top chunk 前面的 chunk 如果不是 fastbin,就必然会和 // top chunk 合并,所以这里设置了 PREV_INUSE。 set_head(victim, nb | PREV_INUSE | (av != &main_arena ? NON_MAIN_ARENA : 0)); set_head(remainder, remainder_size | PREV_INUSE);
check_malloced_chunk(av, victim, nb); void *p = chunk2mem(victim); alloc_perturb(p, bytes); return p; } // 否则,判断是否有 fast chunk /* When we are using atomic ops to free fast chunks we can get here for all block sizes. */ elseif (have_fastchunks(av)) { // 先执行一次fast bin的合并 malloc_consolidate(av); /* restore original bin index */ // 判断需要的chunk是在small bin范围内还是large bin范围内 // 并计算对应的索引 // 等待下次再看看是否可以 if (in_smallbin_range(nb)) idx = smallbin_index(nb); else idx = largebin_index(nb); }
/* calloc(size_t n_elements, size_t element_size); Returns a pointer to n_elements * element_size bytes, with all locations set to zero. */ void* __libc_calloc(size_t, size_t);
sysmalloc
正如该函数头的注释所言,该函数用于当前堆内存不足时,需要向系统申请更多的内存。
1 2 3 4 5 6
/* sysmalloc handles malloc cases requiring more memory from the system. On entry, it is assumed that av->top does not have enough space to service request for nb bytes, thus requiring that av->top be extended or replaced. */
staticvoid *sysmalloc(INTERNAL_SIZE_T nb, mstate av) { mchunkptr old_top; /* incoming value of av->top */ INTERNAL_SIZE_T old_size; /* its size */ char *old_end; /* its end address */
long size; /* arg to first MORECORE or mmap call */ char *brk; /* return value from MORECORE */
long correction; /* arg to 2nd MORECORE call */ char *snd_brk; /* 2nd return val */
INTERNAL_SIZE_T front_misalign; /* unusable bytes at front of new space */ INTERNAL_SIZE_T end_misalign; /* partial page left at end of new space */ char *aligned_brk; /* aligned offset into brk */
mchunkptr p; /* the allocated/returned chunk */ mchunkptr remainder; /* remainder frOm allocation */ unsignedlong remainder_size; /* its size */
/* Little security check which won't hurt performance: the allocator never wrapps around at the end of the address space. Therefore we can exclude some size values which might appear here by accident or by "design" from some intruder. */ // 指针不能指向非法的地址, 必须小于等于-size,为什么??? // 指针必须得对齐,2*SIZE_SZ 这个对齐得仔细想想 if (__builtin_expect((uintptr_t) p > (uintptr_t) -size, 0) || __builtin_expect(misaligned_chunk(p), 0)) { errstr = "free(): invalid pointer"; errout: if (!have_lock && locked) __libc_lock_unlock(av->mutex); malloc_printerr(check_action, errstr, chunk2mem(p), av); return; } /* We know that each chunk is at least MINSIZE bytes in size or a multiple of MALLOC_ALIGNMENT. */ // 大小没有最小的chunk大,或者说,大小不是MALLOC_ALIGNMENT的整数倍 if (__glibc_unlikely(size < MINSIZE || !aligned_OK(size))) { errstr = "free(): invalid size"; goto errout; } // 检查该chunk是否处于使用状态,非调试状态下没有作用 check_inuse_chunk(av, p);
/* If eligible, place chunk on a fastbin so it can be found and used quickly in malloc. */
if ((unsignedlong) (size) <= (unsignedlong) (get_max_fast())
#if TRIM_FASTBINS /* If TRIM_FASTBINS set, don't place chunks bordering top into fastbins */ //默认 #define TRIM_FASTBINS 0,因此默认情况下下面的语句不会执行 // 如果当前chunk是fast chunk,并且下一个chunk是top chunk,则不能插入 && (chunk_at_offset(p, size) != av->top) #endif ) { // 下一个chunk的大小不能小于两倍的SIZE_SZ,并且 // 下一个chunk的大小不能大于system_mem, 一般为132k // 如果出现这样的情况,就报错。 if (__builtin_expect( chunksize_nomask(chunk_at_offset(p, size)) <= 2 * SIZE_SZ, 0) || __builtin_expect( chunksize(chunk_at_offset(p, size)) >= av->system_mem, 0)) { /* We might not have a lock at this point and concurrent modifications of system_mem might have let to a false positive. Redo the test after getting the lock. */ if (have_lock || ({ assert(locked == 0); __libc_lock_lock(av->mutex); locked = 1; chunksize_nomask(chunk_at_offset(p, size)) <= 2 * SIZE_SZ || chunksize(chunk_at_offset(p, size)) >= av->system_mem; })) { errstr = "free(): invalid next size (fast)"; goto errout; } if (!have_lock) { __libc_lock_unlock(av->mutex); locked = 0; } } // 将chunk的mem部分全部设置为perturb_byte free_perturb(chunk2mem(p), size - 2 * SIZE_SZ); // 设置fast chunk的标记位 set_fastchunks(av); // 根据大小获取fast bin的索引 unsignedint idx = fastbin_index(size); // 获取对应fastbin的头指针,被初始化后为NULL。 fb = &fastbin(av, idx);
/* Atomically link P to its fastbin: P->FD = *FB; *FB = P; */ // 使用原子操作将P插入到链表中 mchunkptr old = *fb, old2; unsignedint old_idx = ~0u; do { /* Check that the top of the bin is not the record we are going to add (i.e., double free). */ // so we can not double free one fastbin chunk // 防止对 fast bin double free if (__builtin_expect(old == p, 0)) { errstr = "double free or corruption (fasttop)"; goto errout; } /* Check that size of fastbin chunk at the top is the same as size of the chunk that we are adding. We can dereference OLD only if we have the lock, otherwise it might have already been deallocated. See use of OLD_IDX below for the actual check. */ if (have_lock && old != NULL) old_idx = fastbin_index(chunksize(old)); p->fd = old2 = old; } while ((old = catomic_compare_and_exchange_val_rel(fb, p, old2)) != old2); // 确保fast bin的加入前与加入后相同 if (have_lock && old != NULL && __builtin_expect(old_idx != idx, 0)) { errstr = "invalid fastbin entry (free)"; goto errout; } }
// 如果下一个chunk不是top chunk if (nextchunk != av->top) { /* get and clear inuse bit */ // 获取下一个 chunk 的使用状态 nextinuse = inuse_bit_at_offset(nextchunk, nextsize); // 如果不在使用,合并,否则清空当前chunk的使用状态。 /* consolidate forward */ if (!nextinuse) { unlink(av, nextchunk, bck, fwd); size += nextsize; } else clear_inuse_bit_at_offset(nextchunk, 0);
/* Place the chunk in unsorted chunk list. Chunks are not placed into regular bins until after they have been given one chance to be used in malloc. */ // 把 chunk 放在 unsorted chunk 链表的头部 bck = unsorted_chunks(av); fwd = bck->fd; // 简单的检查 if (__glibc_unlikely(fwd->bk != bck)) { errstr = "free(): corrupted unsorted chunks"; goto errout; } p->fd = fwd; p->bk = bck; // 如果是 large chunk,那就设置nextsize指针字段为NULL。 if (!in_smallbin_range(size)) { p->fd_nextsize = NULL; p->bk_nextsize = NULL; } bck->fd = p; fwd->bk = p;
/* If the chunk borders the current high end of memory, consolidate into top */ // 如果要释放的chunk的下一个chunk是top chunk,那就合并到 top chunk else { size += nextsize; set_head(p, size | PREV_INUSE); av->top = p; check_chunk(av, p); }
/* If freeing a large space, consolidate possibly-surrounding chunks. Then, if the total unused topmost memory exceeds trim threshold, ask malloc_trim to reduce top. Unless max_fast is 0, we don't know if there are fastbins bordering top, so we cannot tell for sure whether threshold has been reached unless fastbins are consolidated. But we don't want to consolidate on each free. As a compromise, consolidation is performed if FASTBIN_CONSOLIDATION_THRESHOLD is reached. */ // 如果合并后的 chunk 的大小大于FASTBIN_CONSOLIDATION_THRESHOLD // 一般合并到 top chunk 都会执行这部分代码。 // 那就向系统返还内存 if ((unsignedlong) (size) >= FASTBIN_CONSOLIDATION_THRESHOLD) { // 如果有 fast chunk 就进行合并 if (have_fastchunks(av)) malloc_consolidate(av); // 主分配区 if (av == &main_arena) { #ifndef MORECORE_CANNOT_TRIM // top chunk 大于当前的收缩阙值 if ((unsignedlong) (chunksize(av->top)) >= (unsignedlong) (mp_.trim_threshold)) systrim(mp_.top_pad, av); #endif// 非主分配区,则直接收缩heap } else { /* Always try heap_trim(), even if the top chunk is not large, because the corresponding heap might go away. */ heap_info *heap = heap_for_ptr(top(av));