glibc动态内存管理

前言

为了更好了解glibc的内存分配机制以及堆溢出类题目,特地来glibc挖坑。

本文章分析的源码来自glibc 2.29

glibc详细信息如下:

  • version: 2.29.9000
  • release: development

GNU分配器简述

翻译自 reference

GNU C库的malloc函数实现来自ptmalloc(pthreads malloc),而pthmalloc又用dlmalloc(Doug Lea malloc)实现。malloc函数有两种不同的内存分配方法,这取决于开辟空间的大小或用户传入的具体参数,最常见的分配方法是从一大块连续的内存区域分配一部分出来,并管理这些相邻的区块以优化其使用和减少浪费不可用块。传统系统的堆被设置为一大块内存区域,但是GNU C库的malloc实现保留了多块这样的内存区域来优化多线程的堆内存使用,每一个这样的内存区域在程序中叫做Arena(竞技场?)

与其它版本相反,GNU C库中的malloc不会将块(chunk)的大小向上取整为2的n次幂,无论块大小如何,相邻的块都可以合并为一块空闲块。这使得malloc的实现适合于各种分配模式且不会在整个段中导致大量的内存浪费。多个Arena的存在使得多个线程能够在独自的Arena中同时分配内存,因此性能得到了提高。

另外一种一种分配方式是用来分配一块很大的内存块的(比一页的大小还要大),此时要完成这种内存分配需要用到mmap(anonymous or via /dev/zero; see Memory-mapped I/O)。这对于释放的这些大块能够立即返还给系统具有很好的优势,因此,即使在调用free释放内存之后,也不会发生大块“锁定”在较小块之间的情况。 mmap的大小阈值是动态的,可以根据程序的分配模式进行调整。 mallopt通过M_MMAP_THRESHOLD可静态调整阈值,而mmap的使用可通过M_MMAP_MAX完全禁用。请参见Malloc Tunable Parameters

GNU Allocator的更详细的技术描述在GNU C库Wiki中。参考https://sourceware.org/glibc/wiki/MallocInternals。 你也可以使用自定义的malloc来代替GNU C库中内置的内存分配器。请参阅Replacing malloc

术语

reference

arena

通过sbrk或mmap系统调用为线程分配的堆区,按线程的类型可以分为2类:

  • main arena:主线程建立的arena;
  • thread arena:子线程建立的arena;

bin

一个用来保存Free chunk链表的表头信息的指针数组,按所悬挂链表的类型可以分为4类:

  • Fast bin
  • Unsorted bin
  • Small bin
  • Large bin

chunk

chunk:逻辑上划分的一小块内存,根据作用不同分为4类:

  • Allocated chunk:即分配给用户且未释放的内存块;
  • Free chunk:即用户已经释放的内存块;
  • Top chunk
  • Last Remainder chunk

三者关系图

在这里插入图片描述

术语详解

arena

在一个或多个线程之间共享的结构,它包含对一个或多个堆的引用,以及这些堆中“空闲”的块的链表。分配给每个arena的线程将从该arena的空闲列表中分配内存。

https://blog.csdn.net/qq_41453285/article/details/96847761

main arena

thread arena

fastbin

链表数组包含最近释放的小块,fastbin并不是双向链表结构,而是更加快速的单向链表结构,因为fastbin的块不会从链表的中间移除,所以也不需要双向链表。fastbin与常规bin不同,常规的bin使用FIFO顺序进行处理,而fastbin使用更快的LIFO的处理顺序,因为在fastbin瞬态使用的上下文环境中排序并不重要。

chunk

source code

// INTERNAL_SIZE_T宏定义为size_t

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

chunk的默认对其方式是以2*sizeof(size_t)字节对其,也就是说在32 bits程序中以2*4=8字节对其,在64 bits程序中以2*8=16字节对齐,其在malloc.c的原文注释如下:

   Alignment:                              2 * sizeof(size_t) (default)
   
   (i.e., 8 byte alignment with 4byte size_t). This suffices for
   nearly all current machines and C compilers. However, you can
   define MALLOC_ALIGNMENT to be wider than this if necessary.

glibc中的chunk的大小

  • 最小的chunk的大小
    chunk需要保证放下mchunk_prev_size、mchunk_size,fd和bk以及符合chunk的对齐方式,因此在32位系统中,最小的chunk为16字节,在64位系统中,最小的chunk为32字节
Minimum allocated size: 4-byte ptrs:  16 bytes    (including 4 overhead)
			  8-byte ptrs:  24/32 bytes (including, 4/8 overhead)

       When a chunk is freed, 12 (for 4byte ptrs) or 20 (for 8 byte
       ptrs but 4 byte size) or 24 (for 8/8) additional bytes are
       needed; 4 (8) for a trailing size field and 8 (16) bytes for
       free list pointers. Thus, the minimum allocatable size is
       16/24/32 bytes.

       Even a request for zero bytes (i.e., malloc(0)) returns a
       pointer to something of the minimum allocatable size.

       The maximum overhead wastage (i.e., number of extra bytes
       allocated than were requested in malloc) is less than or equal
       to the minimum size, except for requests >= mmap_threshold that
       are serviced via mmap(), where the worst case wastage is 2 *
       sizeof(size_t) bytes plus the remainder from a system page (the
       minimal mmap unit); typically 4096 or 8192 bytes.
  • 最大块的大小
  Maximum allocated size:  4-byte size_t: 2^32 minus about two pages
			   8-byte size_t: 2^64 minus about two pages

       It is assumed that (possibly signed) size_t values suffice to
       represent chunk sizes. `Possibly signed' is due to the fact
       that `size_t' may be defined on a system as either a signed or
       an unsigned type. The ISO C standard says that it must be
       unsigned, but a few systems are known not to adhere to this.
       Additionally, even when size_t is unsigned, sbrk (which is by
       default used to obtain memory from system) accepts signed
       arguments, and may not be able to handle size_t-wide arguments
       with negative sign bit.  Generally, values that would
       appear as negative after accounting for overhead and alignment
       are supported only via mmap(), which does not have this
       limitation.

       Requests for sizes outside the allowed range will perform an optional
       failure action and then return null. (Requests may also
       also fail because a system is out of memory.)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值