ptmalloc

发布时间 2023-07-23 23:03:48作者: ho966

1ptmalloc3个层级:arenabinchunk

1) arena

a) arena是内存分配区,主线程会创建main arena, 其他线程会创建thread arena, 也就是存在多个arena,这样可以避免锁的竞争。main arena会通过sbrk()来扩容,它始终是一个连续的内存块。thread arena 不是说每一个线程都是创建自身分配区,它的数量有上限,32bitcpu核心数的2倍,64bit是核心数的8倍,当它的空间不够时,它通过mmap()重新申请一块内存来实现扩容,所以thread arena的内存区域有可能不连续,它通过内部的heap_info结构体来管理多个内存区域。

 

 

b) 上图中有个malloc_state结构体实例,它是用来存放arena的信息,单向链表,串联了不同的arena, 它包含锁,bins, topchunk,remainder chunk等信息。其中main_arenamalloc_state是全局静态实例,因此存放在.data区,thread arenamalloc_state实例存放在自身堆中。

struct malloc_state {
  /* Serialize access.  */
  mutex_t mutex;
  /* Flags (formerly in max_fast).  */
  int flags;
#if THREAD_STATS
  /* Statistics for locking.  Only used if THREAD_STATS is defined.  */
  long stat_lock_direct, stat_lock_loop, stat_lock_wait;
#endif
  /* 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;
#ifdef PER_THREAD
  /* Linked list for free arenas.  */
  struct malloc_state *next_free;
#endif
  /* Memory allocated from the system in this arena.  */
  INTERNAL_SIZE_T system_mem;
  INTERNAL_SIZE_T max_system_mem;
};

 

c) thread arena空间扩展是通过mmap实现的,因此内存区域不是连续的,每块内存区域对应一个head_info结构体,通过单向链表串联起来。heap_info结构体数据也是存放在自身堆中。

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 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;

 

2) chunk

chunk是最小的malloc分配单元,arena内存区域会分成多个大小不一的chunk,chunk的结构体视图如下,

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;
};

 

这个结构体声明有一定的误导性,从上述注释字段可以看出,有些字段在不同场景是不使用的,例如prev_size在前一个chunk是空闲的才使用此字段,fd和bk只有在本chunk空闲时才会被使用,chunk实现了使用边界标记的方式分割内存块。

已分配的chunk如下,size字段的后3位分别代表

N--NON_MAIN_ARENA 0表示属于main arena, 1表示自身属于thread arena

M--IS_MMAPED  0表示是非直接通过mmap获取的内存,1表示通过mmap获取的内存,释放用unmap

P--PREV_INUSE 0表示前一个chunk未被使用,1表示已使用

chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |             Size of previous chunk, if allocated            | |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |             Size of chunk, in bytes                      |N|M|P|
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |             User data starts here...                          .
    .                                                               .
    .             (malloc_usable_size() bytes)                      .
    .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |             Size of chunk                                     |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

空闲的chunk会有双向链表相连(fast bins为了更快的操作内存,使用单向链表,只用到了fd字段),如下:

chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |             Size of previous chunk                            |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `head:' |             Size of chunk, in bytes                         |P|
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |             Forward pointer to next chunk in list             |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |             Back pointer to previous chunk in list            |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |             Unused space (may be 0 bytes long)                .
    .                                                               .
    .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `foot:' |             Size of chunk, in bytes                           |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

 

3) bin 是一个链表,它会串联不同类型的chunk,从而将其管理起来,根据不同大小的chunkbin有几种:fastbinsmallbinlargebinunsortedbin

a) fast bin  

malloc_state中的fastbinsY[NFASTBINS],NFASTBINS为10,每一个元素都是一个单向链表,存放对应大小的空闲chunk,对于linux64bit系统来说,最小是32B,然后每个数组下标递增16B。对于小块内存申请,直接从fast bin中获取,释放也直接存回fast bin。对比其他三种bin,为追求高效,只有fast bin使用chunk的单向链表,通过cas操作增删chunk指针。

b) small bin

malloc_state中的 bins[NBINS * 2 - 2],NBINS 宏定义为128,每个

c) 

 

2malloc流程

1) 找内存分配器arena

查找线程本地存储key值为arena_key对应的malloc_state结构体指针,即查找arena,如果沒有arena,创建新的thread arena(main_arena在ptmalloc_init()中被存入arena_key), 首次通过mmap申请的内存区不小于32k,不大于32bit512K/64bit32M。最后再获取锁。

2) 规范化入参长度size

判断申请size是否超过上限值(unsigned long - 2*最小长度),是否小于最小长度(chunk结构体前四个指针的长度,32位系统16字节,64位系统32字节),是否对齐(对齐长度为2*sizeof(size_t), 32位系统8字节,64位系统16字节)

3) fastbin

申请size如果小于max_fast(默认值32bit64字节/64bit128字节,可通过mallopt设置),则从fast bins上寻找合适的chunk,如果未找到则继续往下走

4) smallbin

申请的size小于1024字节(32512字节),从small bins寻找合适的chunk

5) 如果size >= 1024,或者small bins未找到合适chunk