目录
前一章,我们讲解了fastbins的空闲链表的分配逻辑。这一章,我们主要讲一下smallbins和unsorted bin的分配逻辑。
smallbins的定义:
smallbins空闲链表的使用条件:
具体的实现步骤如下:
如果符合smallbin的大小,则从smallbin的数组上获取一个chunk进行内存分配
如果是smallbin,则通过bins的数组下标获取到对应的chunk双向链表
如果空闲链表中有空闲的chunk,然后从链表的尾部通过弹出一个chunk,并修改双向链表
如果空闲链表中没有空闲的chunk,则此次分配失败,需要跳出smallbins上的分配逻辑,往下走其他逻辑的分配方式
获取得到的victim就是要操作的内存chunk对象,通过chunk2mem和alloc_perturb函数,初始化对象
- /*
- * 如果符合smallbin的大小,则从smallbin的数组上获取一个chunk进行内存分配,如果存在空闲chunk则分配成功;如果空闲链表为空,则分配失败需要往下走
- * 1. 如果是smallbin,则通过bins的数组下标获取到对应的chunk双向链表
- * 2. 然后从链表的尾部通过弹出一个chunk,并修改双向链表
- * 3. 获取得到的victim就是要操作的内存chunk对象,通过chunk2mem和alloc_perturb函数,初始化对象 */
- if (in_smallbin_range (nb))
- {
- idx = smallbin_index (nb); //获取smallbin的数组下标
- bin = bin_at (av, idx); //av->获取数组上的bin ; bins数组为:mchunkptr结构的数组
-
- //只有bin这个chunk双向链表有2个以上值,才会分配
- //bin是mchunkptr结构的链表(双向链表);last(bin) 获取bin->bk,获取最后一个chunk;从链表的尾部,取一个victim
- //Case1: A -> B -> C -> D 四个chunk,则取出D,调整双向链表:A -> B -> C
- //Case2: A -> B 两个chunk,则取出B,调整双向链表:A->bk = A / A->fd = A
- if ((victim = last (bin)) != bin)
- {
- bck = victim->bk; //获取victim的前一个bin
- if (__glibc_unlikely (bck->fd != victim))
- malloc_printerr ("malloc(): smallbin double linked list corrupted");
- set_inuse_bit_at_offset (victim, nb); //将victim设置为已使用状态
- bin->bk = bck; //将bin->bk设置为bck
- bck->fd = bin; //将bck->fd设置为bin
-
- if (av != &main_arena)
- set_non_main_arena (victim);
- check_malloced_chunk (av, victim, nb);
-
- ..................简
-
- void *p = chunk2mem (victim); //通过chunk结构,获取data数据部分的指针地址
- alloc_perturb (p, bytes); //初始化内存块
- return p;
- }
- }
如果是largebin,则会把fast bin中的chunk进行一次整理合并。然后将合并后的chunk放入unsorted bin中,这是通过malloc_consolidate这个函数完成。核心逻辑就是进行一次碎片化整理。
malloc_consolidate具体不展开了,核心处理逻辑:
- /*
- 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.
- */
-
- else
- {
- /* 如果是largebin,则会把fast bin中的chunk进行一次整理合并
- * 然后将合并后的chunk放入unsorted bin中,这是通过malloc_consolidate这个函数完成*/
- idx = largebin_index (nb);
- if (atomic_load_relaxed (&av->have_fastchunks))
- malloc_consolidate (av);
- }
unsorted bin的定义:
核心处理流程:
从unsorted bin的双向链表上去循环遍历每一个未存储的chunk,进行内存分配或者重新放置到smallbins和largebins上去
如果,我们分配的是一个small类型的,遇到合适大小的chunk,则可以将chunk进行切割,并且切割后返回分配的内存chunk
如果,请求的大小,正好和chunk大小匹配,则直接分配成功
如果,上面两步的分配都不成功,则将该chunk根据大小类型,放置到smallbins或者largebins上去
最后,继续开始循环迭代操作
- //尝试从unsorted bin中分配
- for (;; )
- {
- int iters = 0;
- //从unsorted bin的双向链表上去循环处理,直到循环回到起始节点
- //unsorted bin放置在av->bins[1]的数组上,也是chunk类型的双向链表
- while ((victim = unsorted_chunks (av)->bk) != unsorted_chunks (av))
- {
- bck = victim->bk; //获取前一个chunk
- //确认当前chunk的大小,因为unsorted bin放置的是整理后的bin,大小并不是由small bin和large bin这种数组下标来决定的
- size = chunksize (victim);
- mchunkptr next = chunk_at_offset (victim, size); //获取内存块上相邻的下一个chunk的地址,这个不是双向链表上的,是内存相邻的chunk
-
- .............简
-
- /*
- * 分配的是small类型的并且小于当前的chunk,则可以进行切割分配
- * 几个条件:
- * 1. 需要符合smallbin
- * 2. unsorted bins上只剩下一个bin
- * 3. 当前的chunk等于last_remainder
- * 4. size需要大于nb,并且分割后的last_remainder,仍然可以使用*/
- if (in_smallbin_range (nb) &&
- bck == unsorted_chunks (av) &&
- victim == av->last_remainder &&
- (unsigned long) (size) > (unsigned long) (nb + MINSIZE))
- {
- /* split and reattach remainder 进行chunk的切割操作 */
- remainder_size = size - nb; //remainder的大小
- remainder = chunk_at_offset (victim, nb); //remainder指针地址
- 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)); //设置mchunk_size 大小
- set_head (remainder, remainder_size | PREV_INUSE); //设置mchunk_size 大小
- set_foot (remainder, remainder_size); //设置下一个chunk->mchunk_prev_size
-
- check_malloced_chunk (av, victim, nb);
- void *p = chunk2mem (victim); //通过chunk结构,获取data数据部分的指针地址
- alloc_perturb (p, bytes); //初始化内存块
- return p;
- }
-
- /* remove from unsorted list */
- if (__glibc_unlikely (bck->fd != victim))
- malloc_printerr ("malloc(): corrupted unsorted chunks 3");
- unsorted_chunks (av)->bk = bck;
- bck->fd = unsorted_chunks (av);
-
- /* Take now instead of binning if exact fit */
- /* 请求的大小,正好和chunk大小匹配,则直接分配成功 */
- if (size == nb)
- {
- set_inuse_bit_at_offset (victim, size); //将victim设置为已使用状态
- if (av != &main_arena)
- set_non_main_arena (victim);
- ............简
- check_malloced_chunk (av, victim, nb);
- void *p = chunk2mem (victim);//通过chunk结构,获取data数据部分的指针地址
- alloc_perturb (p, bytes);//初始化内存块
- return p;
-
- }
-
- /* place chunk in bin */
- //当前chunk属于small bin的范围,将其放回small bin
- if (in_smallbin_range (size))
- {
- victim_index = smallbin_index (size);
- bck = bin_at (av, victim_index);
- fwd = bck->fd;
- }
- else //当前chunk属于large bin的范围,将其放回large bin
- {
- victim_index = largebin_index (size);
- bck = bin_at (av, victim_index);
- fwd = bck->fd;
-
- ................简
-
- mark_bin (av, victim_index);
- // bck -> fwd 插入victim bck -> victim -> fwd
- victim->bk = bck;
- victim->fd = fwd;
- fwd->bk = victim;
- bck->fd = victim;
-
-
-
- #define MAX_ITERS 10000
- //循环最多10000次,则结束
- if (++iters >= MAX_ITERS)
- break;
- }
下一章节,我们继续看_int_malloc函数里面的largebins和topchunk的实现