转载自:vector03
4 释放与实现
释放过程相对分配就简单多了, 基本着重在chunk合并, top裁剪, segment释放上. dlmalloc中合并是减少外部碎片最有效的方法了.
4.1 dlfree
释放的主要过程就是根据用户传入的payload, 找到chunk指针, 然后分别检查前一个和后一个chunk是否可以合并. 这里唯一需要注意的就是与dv和top这些特殊chunk的交互.
基本流程如下,
- 通过用户传入的mem指针计算出chunk指针p. 如果FOOTERS打开, 则通过magic计算出其所属的mspace指针, 并进行校验.
- 若p是通过direct mmap生成的, 则还原其头尾的fake chunk后直接munmap释放并结束. 详细内容请参考3.4.2小节的说明.
- 若p的prev chunk也是free chunk则将p和prev合并. 若prev同时又是dv, 则还需要考虑p的next chunk. 假如next是inused chunk, 则直接将合并后的p替换为新的dv并返回, 否则进入下一步.
若p的next chunk也是空闲的, 则又分为三种情况,
a. next是普通的free chunk, 与p进行合并. 如果p同时是dv, 则更新dv.b. next同时是top, 则与p合并后更新top为p. 如果p同时又是dv, 则取消当前记录的dv(相当于dv被top吞并了). 若top已经超出trim阈值, 则执行sys_trim.
c. next同时是dv, 则与p合并后更新dv为p.
若p是经历前面步骤的普通chunk, 则将更新后的p重新插入分箱系统. 如果realse_check满足, 则检查并回收当前mspace下所有的free segment.
代码注释如下,
4.2 sys_trim
当dlmalloc在执行free请求时, 会检测当前top剩余空间是否超出trim_check规定的阈值. 如果是就会尝试收缩当前的top空间. 默认情况下, dlmalloc会保留一个粒度(granularity)大小的空间, 剩余的都将归还给系统, 可以传入参数pad指定额外的剩余空间(多数情况下是0). 另外, 由于top所在区段有可能位于heap区或mmap区, 因此也会有不同的收缩方式. 对于heap区的top空间, 采取反向MORECORE的方式, 而对于mmap区的, 则先尝试用mremap进行收缩, 如果失败则使用mumap释放掉. 假设遇到trim失败的情况, dlmalloc就会自动关闭auto-trimming功能.
源码注释如下,
4.3 release_unused_segments
尽管有auto-trimming压缩top空间, 但多数情况下, 只依靠这种方法是无法满足内存释放需求的. 尤其是当外部碎片导致top不连续的情况下, auto-trimming可能相当一段时间无法触发. 此时, dlmalloc就转而寻找内部可回收的空闲段. 由于查找空闲段是一个耗时操作, 且出现的频率较低, 所以实际上按照一个周期来进行此操作. 当周期计数为0时, 就调用release_unused_segments.
判断一个区段是否空闲也比较简单, 因为空闲chunk合并的原因, 若当前段的第一个chunk为空闲, 且其大小覆盖整个区段除隐藏区域的全部范围, 就可以判定该区段为空闲段. 接下来只要unlink空闲chunk, 且munmap该区段即可. 源码注释如下,