堆详述(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实现。
    在这里插入图片描述

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()
    控制起始界限

mmap()说明

详细调用过程

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流程(网上有这张原图)

有需要可私聊分享。
在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值