zoukankan      html  css  js  c++  java
  • [置顶] NGINX原理分析之SLAB分配机制

    一、基础概述

        如果使用伙伴系统分配和释放算法,不仅会造成大量的内存碎片,同时处理效率也比较低。SLAB是一种内存管理机制,其核心思想是预分配。SLAB是将空间按照SIZE对内存进行分类管理的,当申请一块大小为size的内存时,SLAB分配器就从size集合中分配一个单元出去,当释放一个大小为size的内存时,则将其放回到size集合中去,但不是返回给操作系统。当又要申请一个size的内存时,可以重复上面的处理,从而避免了内存碎片的产生。[注:因SLAB处理过程中,涉及的细节太多,在此只是做一个原理上的分析]

    二、总体结构


    图1 SLAB内存结构

    三、处理流程

        如图1中所示:SLAB机制管理内存时,其将内存大体上分为SLAB头、SLOT数组、PAGES数组、可分配空间、浪费的空间。以下将对各内存块的作用和处理过程做详细的分析。

    3.1 初始化流程

        头部汇总了SLAB内存管理机制的基础数据,这些基础数据是内存分配、地址计算等处理的依据,SLAB头结构体如代码1所示:
    typedef struct {
        size_t            min_size;     /* 最小分配单元 */
        size_t            min_shift;    /* 最小分配单元对应的位移 */
    
        ngx_slab_page_t  *pages;        /* 页数组 */
        ngx_slab_page_t   free;         /* 空闲页链表 */
    
        u_char           *start;        /* 可分配空间的起始地址 */
        u_char           *end;          /* 可分配空间的结束地址 */
    
        ...                             /* 其他变量成员(省略) */
    }ngx_slab_pool_t

    代码1 SLAB头部结构体

        初始化过程中,对如下数据进行处理:

        ①、SLOT数组:计算SLOT数组长度、并设置数组成员变量初始数据;

        ②、PAGES数组:计算PAGES数组长度、并设置数组成员的变量初始数据;

        ③、可分配空间:按照每页4K的大小,计算可分配空间的页数,不足一页(4K)的空间将会被舍弃;

        ④、SLAB头:设置其成员变量,如图1中指针所示,其中成员变量free将会指向可分配空间的首地址。

    3.2 页的管理

    3.2.1 页的分配

    1)分配之前

        在SLAB初始化之后,所有页可以看成是一个连续的整体,其内存结构如下图所示:

    图2 页的结构(分配之前)

    2)申请一页

        当申请一页时,则将pages[0]从free链表中分离出去,如下图所示:

    图3 页的结构(申请一页)

    3)申请二页

        当再申请二页时,则将page[3]和pages[4]作为一个整体从free链表中分离出去,如下图所示:

    图4 页的结构(申请二页)

    3.2.2 页的回收

    1)回收一页

        当页被回收时,被回收的页并不会和未被分配的页进行合并,而是通过链表串联起来,如下图所示:

    图5 页的结构(回收一页)

    2)回收二页

        当页被回收时,被回收的页并不会和未被分配的页进行合并,而是通过链表串联起来,如下图所示:

    图6 页的结构(回收二页)

    3.4 SLOT的管理

        SLOT数组长度N为8,各成员依次负责8(1~8)、169~16)、3217~32)、6433~64)、12865~128)、256129~256)、512257~512)、1023513~1023)字节内存块的分配和回收。

        SLOT数组各成员相当于链表头,在SLOT的分配和回收过程中,通过链表来组织用于分配各SIZE的PAGE。如,在某时刻,可能存在如下状态:

    图7  SLOT和PAGES的关系

    3.4.1 页的管理

    1)初始状态

        在SLAB初始化后,slot链表头的下一个节点都为NULL,如下图所示:

    图7 SLOT初始状态

    2)添加一页

        SLOT[2]负责32(17~32)字节空间的分配和回收,假设现申请分配24字节(17~32之间)的空间,因此将从slot[2]中分配。但在初始状态下slot[2]的下一页为NULL,因此需要向页管理模块申请一页pages[x]内存,再将该页加入到slot[2]的链表中,添加之后的内存结构如下图所示:

    图8 slot[2]增加一页

    3)暂离链表

       SLOT[2]中的每一页有128(4K/32=128)个单元,当一页分配了128次时,表示该页可分配单元分配完毕,此时该页将会暂时从链表中剔除出去,以防止下次申请时,做无效的遍历。如下图所示:

    图9 slot[2]第一页被使用完

    4)再添一页

        当再次申请17~32字节时,此时slot[2]的后续链表为空,因此需要再次向页管理申请一页pages[y]内存,再将该页加入到slot[2]的链表中,如下图所示。如果该页又被分配完,则进行3)的处理。

    图10 slot[2]再添一页

    5)重入链表

          当所有单元被用完的页pages[x]中的一个单元被回收时,页pages[x]中将有1个单元可以再次被分配使用,此时应该将pages[x]重新加入到slot[2]的链表中,以便下次分配时可以从页pages[x]中进行查找。此时内存组织形式如下图所示:

    图11 页pages[x]重入链表

    6)回收整页

        当页pages[x]所有单元被释放后,则该页将会被全部回收:该页将从slot[2]的链表中被剔除,并将页pages[x]重新加入到free链表。此时的内存结构图如下图所示:

    图12 回收页pages[x]

    3.4.2 SLOT的分配

    1)页内结构

        被加入到SLOT数组链表的页在逻辑上划分为很多的内存单元,每一小内存单元的使用情况是通过位图进行标记的,1表示被占用,0表示未被占用。如:第20位bit的值为1时,表示第20个内存单元被占用。假如此SLOT链表的PAGE正好可以划分为32块,则其逻辑组织结构如下图所示:

    图13 PAGE内结构

    2)分配单元

        假如此时在SLOT[s]链表的页中连续申请4个内存单元,则其前4个内存单元将首先被占用,则此时的位图结构如下图所示:

    图14 分配单元

    3)释放单元

        假如此时释放SLOT[s]链表页中第3个内存单元,则此时的位图结构如下图所示:

    图15 释放单元

    —— 邹祁峰

    2013.09.15 23:39

  • 相关阅读:
    《算法导论》读书笔记
    【原创】POI操作Excel导入导出工具类ExcelUtil
    10-JMM
    09-字节码执行引擎
    08-类加载机制
    07-前端编译与优化(待补充)
    06-字节码指令
    05-类文件结构
    04-垃圾回收(2)
    03-垃圾回收(1)
  • 原文地址:https://www.cnblogs.com/suncoolcat/p/3324880.html
Copyright © 2011-2022 走看看