Linux堆管理机制基本原理

概述

Linux中malloc的早期版本是由Doug Lea实现的,Wolfram Gloger在Doug Lea的基础上改进使得Glibc的malloc可以支持多线程–ptmalloc。

ptmalloc实现了malloc(),free()以及一组其他的函数,以提供动态内存管理的支持。

main arena和non main arena

在Doug Lea实现的内存分配器中值有一个主分配区(main arena),每次分配内存都必须对主分配区加锁,分配完后释放锁,在多线程的环境下因为对锁的竞争导致分配效率比较低。

为此,Wolfram Gloger进行改进增加了非主分配区(non main arena)使得Glibc的malloc可以支持多线程。

每个进程只有一个主分配区,但是可能存在多个非主分配区。

主分配区可以访问进程的heap区域和mmap映射区域

非主分配区只能访问进程的mmap映射区域,不可以访问heap区域

chunk

通常,用户请求分配的空间在ptmalloc中使用一个chunk来表示,当用户调用free()函数的时候也不是立即归还给操作系统,同样也表示为一个chunk,由内存分配器ptmalloc进行管理。

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

ptmalloc在给用户分配的空间的前后加上一些控制信息,这样可以方便分配和释放工作。

    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of previous chunk, if unallocated (P clear)  |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of chunk, in bytes                     |A|M|P|
      mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             User data starts here...                          .
            .                                                               .
            .             (malloc_usable_size() bytes)                      .
            .                                                               |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             (size of chunk, but used for application data)    |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of next chunk, in bytes                |A|0|1|
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

chunk指针指向一个chunk的开始,一个chunk包含了用户请求的内存区域和相关控制信息

mem指针才是真正返回给用户的指针,也就是通常我们使用malloc()返回的指针

chunk的第二个域size的第三位分别为AMP

  • P 表示前一个chunk是否在使用,使用为1,没有使用为0
  • M 表示当前chunk是否是mmap映射区域分配的,是为1,不是为0
  • A 表示当前chunk是否属于非主分配区,是为1,不是为0

空闲的chunk

    chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of previous chunk, if unallocated (P clear)  |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    `head:' |             Size of chunk, in bytes                     |A|0|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                           |
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
            |             Size of next chunk, in bytes                |A|0|0|
            +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

当我们使用free()函数释放内存时候,释放的内存就表示为一个空闲的chunk

空闲的chunk中,不存在M位,只存在AP位,还有一个与使用中的chunk不一样的地方在于原来的用户数据区的地方存放了四个指针

  • fd指针指向后一个空闲的chunk
  • bk指针指向前一个空闲的chunk
  • fd_nextsize
  • bk_nextsize

chunk的空间复用

其实同一个chunk的两种状态,就是空间复用的一种表现,四个指针在用户区域的指针在用户使用内存的时候是不存在的,还有下一个chunk的prev_size也是不存在的,这些都划为用户区域;在chunk空闲的时候,这四个指针prev_size就有效了,方便管理空闲的chunk。

空闲chunk管理

Fast Bins

通常情况下,程序在运行时会经常申请和释放一些较小的内存空间,ptmalloc为了加快分配的效率,引入了fast bins,fast bins中的chunk并不改变它的使用标志位P,当用户需要小内存的时候,优先从fast bins中查找合适的chunk。

Bins

ptmalloc将相似大小的chunk用双向链表链接起来,这样的一个链表称为一个bin。

ptmalloc一共维护了128bin,并使用一个数组来存储这些bin。

数组中第一个bin为Unsorted Bin,从数组的第二个开始共64个bin为small bin,剩下的称为large bin

Unsorted Bin

unsorted bin使用bins数组的第一个,当用户释放在chunk大小不在fast bins范围,这些chunk就会首先被放到unsorted bin中

猜你喜欢

转载自blog.csdn.net/weixin_43833642/article/details/106690765