在glibc源码中搜索split
可以找到堆块切割相关源码
在malloc.c
的_int_malloc
函数中发生堆块切割的情形如下:
- 遍历unsorted chunks时
- 请求满足small request
- last remainder 堆块是唯一堆块,且切割后剩下的仍然满足最小堆块大小
- 从largebin中找到最小的满足要求的堆块
- 切割后剩余堆块的大小满足最小堆块大小
- 从bin中找到最小的满足要求的堆块
- 切割后剩余堆块的大小满足最小堆块大小
- 从 Top Chunk 切割(不讨论这种情况,因为我暂时没用到)
遍历 unsorted chunks
/*
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.
*/
if (in_smallbin_range (nb) &&
bck == unsorted_chunks (av) &&
victim == av->last_remainder &&
(unsigned long) (size) > (unsigned long) (nb + MINSIZE))
{
/* split and reattach remainder */
remainder_size = size - nb;
remainder = chunk_at_offset (victim, nb);
unsorted_chunks (av)->bk = unsorted_chunks (av)->fd = remainder;
av->last_remainder = remainder;
remainder->bk = remainder->fd = unsorted_chunks (av);
if (!in_smallbin_range (remainder_size))
{
remainder->fd_nextsize = NULL;
remainder->bk_nextsize = NULL;
}
set_head (victim, nb | PREV_INUSE |
(av != &main_arena ? NON_MAIN_ARENA : 0));
set_head (remainder, remainder_size | PREV_INUSE);
set_foot (remainder, remainder_size);
check_malloced_chunk (av, victim, nb);
void *p = chunk2mem (victim);
alloc_perturb (p, bytes);
return p;
}
last remainder堆块是上次切割后剩下的堆块
- 请求的大小满足small request,即属于small bin大小
- last remainder堆块是唯一堆块,且切割后剩下的仍然满足最小堆块大小
如果遍历unsorted chunks时满足以上条件,就从last remainder切割出来,剩下的作为新的last remainder堆块
查找 largebin
/*
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))
{
...
remainder_size = size - nb;
unlink_chunk (av, victim);
/* Exhaust */
if (remainder_size < MINSIZE)
...
/* Split */
else
...
对于满足要求的堆块,如果切割后剩余堆块的大小不足最小堆块大小,则整个取出(Exhaust),否则进行切割(Split)。
从bin查找较大的chunk
/*
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.
*/
...
size = chunksize (victim);
/* We know the first chunk in this bin is big enough to use. */
assert ((unsigned long) (size) >= (unsigned long) (nb));
remainder_size = size - nb;
/* unlink */
unlink_chunk (av, victim);
/* Exhaust */
if (remainder_size < MINSIZE)
{
set_inuse_bit_at_offset (victim, size);
if (av != &main_arena)
set_non_main_arena (victim);
}
/* Split */
else
...
}
从下一个最大的bin开始查找合适的堆块,查找和切割方式和largebin已有。