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