1、ptmalloc的3个层级:arena、bin、chunk
1) arena
a) arena是内存分配区,主线程会创建main arena, 其他线程会创建thread arena, 也就是存在多个arena,这样可以避免锁的竞争。main arena会通过sbrk()来扩容,它始终是一个连续的内存块。thread arena 不是说每一个线程都是创建自身分配区,它的数量有上限,32bit是cpu核心数的2倍,64bit是核心数的8倍,当它的空间不够时,它通过mmap()重新申请一块内存来实现扩容,所以thread arena的内存区域有可能不连续,它通过内部的heap_info结构体来管理多个内存区域。
b) 上图中有个malloc_state结构体实例,它是用来存放arena的信息,单向链表,串联了不同的arena, 它包含锁,bins, topchunk,remainder chunk等信息。其中main_arena的malloc_state是全局静态实例,因此存放在.data区,thread arena的malloc_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,从而将其管理起来,根据不同大小的chunk,bin有几种:fastbin,smallbin,largebin,unsortedbin。
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)
2、malloc流程
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字节(32位512字节),从small bins寻找合适的chunk
5) 如果size >= 1024,或者small bins未找到合适chunk
标签:ptmalloc,arena,malloc,chunk,内存,+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ From: https://www.cnblogs.com/ho966/p/17576099.html