Rosalloc简要分析

1. 关键数据结构

i. FreePageRun

描述将要用来分配的地址空间(逻辑地址空间,下同)。

ii. Run

相同大小的slot的集合。

iii. Slot

内存分配单位。
对于run的大小可分为34个标准(bracketSizes):标号0-31的,大小为16*(i+1)个字节,其中i为标号值,32大小为1kB,33为2kB。
不同标号的run所用page数目也不一样(numOfPages):0-3 的run大小为1 page,4-7的为2 page,8-15的为4 page,16-31的为8 page,32的16 page, 33的32 page。
每个run中slot的个数可以计算出。

2. 相关操作

i. 构造函数

  • Initialize
    初始化bracketSizes,numOfPages等等。

  • 其他
    初始化free_page_runs_,第一个free page run的地址通过够在函数得到,为rosalloc space的地址,大小为capacity_。
    初始化其他成员变量。

ii. Alloc

  • AllocLargeObject
    如果所需分配字节数超过kLargeSizeThreshold(2048),则分配大对象。大对象内存直接通过alloc pages获得。

  • AllocFromRun
    如果需要分配的长度小于192字节(级别11以下),从thread local中分配。如果超过这个值,调用AllocFromCurrentRunUnlocked函数,从共用中分配。

  • AllocFromCurrentRunUnlocked
    current_runs_存放了各个编号的当前可用来分配的run。Alloc函数首先从current_runs_得到即将用于分配的run,分配该run中的一个slot。Run中slot是通过bitmap管理,分配的时候通过bitmap计算得到空闲slot的地址,bitmap标记后返回该slot地址。如果在current run中分配失败,说明该run已满,需要调用RefillRun重新获得一个run,并将新的run记录为current run,然后重新分配slot。

  • RefillRun
    non_full_runs_中存放了既没有全部使用完,也不是current的run。Refill run的时候优先从non_full_runs_中获得,如果non_full_runs_没有该编号的run,就通过AllocRun获得一个全新的run。

  • AllocRun
    该函数主要做两件事:

    1. 通过AllocPages获得一定数目的page(page数目根据编号确定);

    2. 对分出来的run进行初始化。如果alloc pages失败,返回Null。

  • AllocPages
    free_page_runs_存放了用于分配page的空间。Alloc page函数从free_page_runs_头开始查找,寻找大于所需求大小的free page run。查找结果可能两种:
    找到合适的free page run,则分割该free page run,并将剩余的作为新的free page run插入free_page_runs_;
    找不到合适的,则在原有的free_page_runs_后面扩展新的free page run结构,使满足所需求的page。扩展前会预先检查确保扩展后不会突破capacity_,扩展的大小最少为2M。扩招再进行分割,满足需求后将剩余的作为新的free page run放入free_page_runs_中去。得到符合要求的pages后,会在page_map_中标注该段pages的用途(详细看代码)。

iii. Free

  • FreeInternal
    根据需要free的ptr,对比page_map_知道该段内存的用途,作为largeObject还是run。如果是largeObject,调用FreePages释放pages;如果是run,调用run的free流程FreeFromRun(run的定位通过page_map_和base计算得到)

  • FreeFromRun
    首先调用FreeSlot,对该run中对应的slot清0,清bitmap。如果归还该run已经空了,没有slot被占用,则从non_full_runs_摘除该run,然后清除该run的header,调用FreePages归还pages。如果该run不为空,说明还有其他slot被占用,检查non_full_runs_是否记录过该run。如果一切run是full的,不在non_full_runs_之中,此时将它加入non_full_runs_。

  • FreePages
    更新对应地址上的page_map_标记为kPageMapEmpty,根据page_release_mode_判断是否要对该领域清0。将释放掉的地址空间构造成一个新的FreePageRun,如果free_page_runs_集合不为空,则尝试着向低地址或者高地址合并,变成新的更大FreePageRun,插入free_page_runs_。此处会releasePages(1、memset清0,2、标记page_map_ ),但不会通过madvice置为MADV_DONTNEED,提高该段内存重用时的性能。

iv. Trim

  • Trim
    获得free_page_runs_最尾部的free page run(未使用),memset清0或者通过madvice置为MADV_DONTNEED,更新footprinter。

i. Thread Local

  • 初始化
    Thread构造函数中会构造一个run* 数组rosalloc_runs。数组大小为34,对应于34个级别的run,不过此时的run并不可用,第一次使用的时候才会真正分配一个run。以后从thread local分配时,从rosalloc_runs取出对应级别的run进行分配。

  • Alloc
    在thread_local_run中调用AllocSlot分配空间。如果第一次分配不到内存,说明是首次调用,或者run已满。如果是run首次调用,refill这个run,如果是run已满,调用MergeThreadLocalFreeBitMapToAllocBitMap函数将thread local free bitmap合并到alloc bitmap中去。 thread local free bitmap在thread local run被free的时候才会被置,如果merge后返回为1(即thread local free bitmap被置为1过),说明thread local run被释放过,但是alloc bitmap还未同步,则更新alloc bitmap的信息,重新在这个run上进行分配。这么做的好处是:小额内存使用时候,不需要在共享内存上分配,防止加锁降低性能。
    如果merge后bitmap显示该run还是full,则重新获得run,并将其作为新的thread local run,在新的thread local run分配slot。

  • Free
    调用MarkThreadLocalFreeBitMap函数,将该部分内存清0,将thread local bitmap中该slot位置置1
    ii. Bulk Free

  • BulkFree
    该函数传进参数为地址数组,函数功能为批量Free。

    1. 根据传进地址,决定释放run或者释放large object(即释放page,非物理page)。如果释放run,标记bulk free bitmap。

    2. 如果释放的run为thread local run,将bulk free bitmap合并到thread local bitmap中去,如果为普通run,将bulk free bitmap合并到alloc bitmap中去。

    3. 如果释放后的run为空并且不为当前使用的run,则free掉该run。

    4. 如果释放后的run不为空且不为当前使用的run,则将该run插入到non_full_runs中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值