Heap
Heap Memory
- malloc
/*
malloc(size_t n)
Returns a pointer to a newly allocated chunk of at least n
bytes, or null if no space is available. Additionally, on
failure, errno is set to ENOMEM on ANSI C systems.
If n is zero, malloc returns a minimum-sized chunk. (The
minimum size is 16 bytes on most 32bit systems, and 24 or 32
bytes on 64bit systems.) On most systems, size_t is an unsigned
type, so calls with negative arguments are interpreted as
requests for huge amounts of space, which will often fail. The
maximum supported value of n differs across systems, but is in
all cases less than the maximum representable value of a
size_t.
*/
申请一块内存,通过指针指向引用,失败则返回ENOMEM。
如果请求分配内存大小n是0,将自动返回一个默认的最小大小块,32位和64位系统大小不同。最大分配空间大小不会超过最大可分配大小。
- free
/*
free(void* p)
Releases the chunk of memory pointed to by p, that had been
previously allocated using malloc or a related routine such as
realloc. It has no effect if p is null. It can have arbitrary
(i.e., bad!) effects if p has already been freed.
Unless disabled (using mallopt), freeing very large spaces will
when possible, automatically trigger operations that give
back unused memory to the system, thus reducing program
footprint.
*/
释放指针p所指向的内存块,如果释放的空间非常大的话,可能会把未使用的内存空间交换给系统。
- malloc和free通过底层系统调用mmap和sbrk实现。
brk() and sbrk() change the location of the program break, which
defines the end of the process's data segment (i.e., the program
break is the first location after the end of the uninitialized
data segment). Increasing the program break has the effect of
allocating memory to the process; decreasing the break
deallocates memory.
这段总结下就是上面标红三个量最初是相等的:
start_brk = brk = end_data
- brk()
控制结束界限 - sbrk()
控制起始界限
glibc malloc
详细参考:
Understanding glibc malloc
涉及的主要知识点概述
堆的glibc实现主要包括struct _heap_info,struct malloc_state,struct malloc_chunk这3个结构体。
- heap_info – Heap Header 。
- malloc_state –
Arena Header
后面介绍。 - malloc_chunk – Chunk Header 。
关于Arena(中文翻译:竞技场)
我们知道一个线程申请的1个/多个堆包含很多的信息:二进制位信息,多个malloc_chunk信息等,这些堆需要东西来进行管理,那么Arena就是来管理线程中的这些堆的。
Arena有主线程和子线程之分
主线程的arnea称为“main_arena”。
子线程的arnea称为“thread_arena”。
注意:
-
Main_Arena没有多个堆,因此没有heap_info结构。当Main_Arena所指向的堆内存不足时,sbrk的堆段被扩展(连续区域),直到它碰到内存映射段。
-
不同于Main_Arena,Main_Arena的Arena标题并不是sbrk heap segment 的一部分,而是一个全局变量!因此它属于libc.so 的数据段。
-
单个堆段
Main Arena:
可以看到Main Arena的顶级成员指向堆块的malloc_chunk结构体,
因为主竞技场没有多个堆,所以没有heap_info结构体。
Thread Arena:
heap_info:表示该堆段的信息,并且该结构体的ar_ptr成员指针指向于该堆段所属的thread arena。
malloc_state:该堆段的竞技场并且顶部指向于最顶端的个malloc_chunk。
chunk块:就是该堆段所存储的区域。 -
多个堆段
Thread Arena:
heap_info:因为有两个堆段,所以每个堆段都有自己的heap_info并且两个堆段在内存中不是物理相邻的,因此第二个heap_info的上一个指针指向于第一个heap_info的ar_ptr成员,而第一个heap_info结构体的ar_ptr成员指向了malloc_state,这样就构成了一个单链表,方便后续管理。
malloc_state:虽然有多个堆段,但是只有一个arena。
Arena的数量限制与多线程管理
Arena数量与核心处理器数量有关:
32位系统中:
Number of arena = 2 * number of cores + 1.
64位系统中:
Number of arena = 8 * number of cores + 1
struct malloc_state(Arena的实现)
这部分属于全局变量
而不属于堆栈,可通过此完成泄露libc地址等操作。
struct malloc_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 */
unsigned int binmap[BINMAPSIZE];
/* Linked list */
struct malloc_state *next;
/* Linked list for free arenas. Access to this field is serialized
by free_list_lock in arena.c. */
struct malloc_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;
};
重要成员:
- last_remainder:当次arena中产生last_remainder时,此成员被初始化,并且指向arnea中的last_remainder
- fastbinsY数组:存储的是该领域管理的fastbins
- bins数组:存储的是该领域管理的smallbins,unsortedbin,largebins
- binmap变量:系统查看有哪些垃圾箱链中有块时,不可能去fastbinsY和箱数组一个一个的遍历通过binmap变量,采用二进制存储,将二进制位与数组的索引相对,系统查找箱链时可以。通过按位与来查询,这样更高效。
struct _heap_info(堆信息结构体)
typedef 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;
重要成员:
- ar_ptr:此堆段归属于哪一个arnea管理
- prev:前一个堆段
struct malloc_chunk(真存储堆数据信息)
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk (if free).*/
INTERNAL_SIZE_T mchunk_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;
};
熟记结构(32位):
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<-(chunk+8)
`head:' | Size of chunk, in bytes |A|0|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+<-(chunk+16)
| 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 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk, in bytes |A|0|0|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
- P (PREV INUSE): 0当前一个块(不是链表中的前一个块,而是内存中它之前的直接块)是空闲的(因此前一个块的大小存储在第一个字段)。分配的第一个块已经设置了这个位。如果它是1,那么我们不能确定前一个块的大小。
- M (IS mmap):通过mmap获取块。其他两位被忽略。映射块既不在竞技场中,也不在自由块的附近。
- A(非主竞技场):主竞技场中的方块为0。每个派生的线程接收自己的竞技场,对于那些块,这个位被设置。
Chunk分类(上图已经用不同颜色区分)
glibc给我们申请的堆块主要分为以下几类:
- allocated chunk。
- free chunk。
- top chunk。
- Last remainder chunk。
每一类就是一个malloc_chunk结构体,因为这些chunk同属于一个堆块,所以在一块连续的内存中,只是通过区域中特定位置的某些标识符加以区分。
为了简便,我们将4个种类的chunk划分为2个种类的chunk,这两个种类的chunk才是我们平常口头上所使用到的:
- allocated chunk:当前chunk是被应用层用户所使用的。
- free chunk:当前chunk是空闲的,没有被应用层用户所使用。
TOP Chunk
- 概念:当一个chunk处于一个arena的最顶部(即最高内存地址处)的时候,就称之为top chunk。
- 作用:该chunk并不属于任何bin,而是在系统当前的所有free chunk(无论哪种bin)都无法满足用户请求的内存大小的时候,将此chunk当做一个应急供给,分配给用户使用。
- 分配的规则:如果top chunk的大小比用户请求的大小要大的话,就将该top chunk分作两部分:
(1)用户请求的chunk;
(2)剩余的部分成为新的top chunk。否则,就需要扩展heap或分配新的heap了——在main arena中通过sbrk扩展heap,而在thread arena中通过mmap分配新的heap。
last remainder的深度剖析
bins(管理内存分配的中间层)
bins概念
-
bin是一个由struct chunk结构体组成的链表。
-
不同的chunk根据特点不同分为不同的chunk,为了将这些chunk进行分类的管理,glibc采用了bin链这种方式管理不同的chunk。
-
不同的bin链是由arena管理的。
-
bin链中的chunk均为free chunk。
-
每个bin由bins数组中的两个值表示。第一个是指向’HEAD’的指针,第二个是指向bin列表的’TAIL’的指针。在快速容器fastbin(单链表)的情况下,第二个值是NULL。
bins分类
根据bin链成员的大小不同,分为以下几类:
- Fast bin。大小:16-80字节。(具体大小分类还得参照不同版本的源码划分,LIFO)
- Unsorted bin(当作cache层)。
- Small bin。大小:小于512字节。(FIFO)
- Large bin。大小:大于512字节。
fast bin是单链表,其他都是双向链表。
fast bin中的chunk被视为已分配的chunk,不会被重新合并。
bins对应管理类型(总共 126个 bins):
- Bin 1 – Unsorted bin
- Bin 2 to Bin 63 – Small bin
- Bin 64 to Bin 126 – Large bin
heap流程(网上有这张原图)
有需要可私聊分享。