mimalloc是微软最近开源的一个malloc实现,其实验数据表明相比于jemalloc、tcmalloc等实现大约快了10%。其通过将空闲块列表(Free List)进行分片(Sharding)来保证分配的内存有更好的空间的局部性,从而提升性能。在mimalloc中一共进行了4次Free List的Sharding。接下来我们会分别介绍这4个Free List的Sharding的位置以及其为什么要进行Free List的Sharding。

在Mimalloc页中进行的Free List的Sharding

在其他的内存分配器的实现中,一般会为每一类大小的对象保留一个Free List,随着内存的不断分配与释放,这个列表中的对象可能散布在整个地址空间中,因此内存分配的局部性较差。而在mimalloc中,其通过将内存分页,每个页负责一个特定大小的内存的分配,每个页有一个Free List,因此内存分配的空间局部性较好。
其他内存分配器的Free List
 

mimalloc的Free List

Local Free List

mimalloc希望能够限制内存分配与内存释放在最差情况下的时间消耗,如果上层应用需要释放一个非常大的结构体,其可能需要递归的释放一些子结构体,因而可能带来非常大的时间消耗。因而在koka与lean语言中,其运行时系统使用引用计数来追踪各种数据,然后保留一个延迟释放的队列(deferred decrement list),当每次释放的内存块超过一定数量后就将剩余需要释放的内存块加入该队列,在之后需要释放的时候再进行释放。那么下一个需要确定的问题是什么时候再去从该队列中释放内存。从延迟释放队列中继续进行释放的时机最好应当是内存分配器需要更多空间的时候,因此这需要内存分配器与上层应用的协作。
在mimalloc中,其提供一个回调函数,当进行了一定次数内存的分配与释放后会主动调用该回调函数来通知上层应用。mimalloc在实现时检测当前进行内存分配的页的Free List是否为空,如果为空则调用该回调,但是为了避免用于一直不断的分配与释放内存,导致Free List一直不为空,而导致回调函数一直得不到回调。因此mimalloc将Free List第二次进行Sharding,将其分为Free List与Local Free List。
当内存在进行分配时会从对应页的Free List中取得内存块,而释放时会将内存块加入Local Free List中,因而在进行一定次数的内存分配后,Free List必定为空,此时可以进行deferred free的回调函数的调用。

Thread Free List

在mimalloc中每个堆都是一个Thread Local的变量,而每次进行内存分配时,其均会从这个Thread Local的堆中进行内存的分配,而释放时即可能从该线程中释放也可能从其他线程中进行释放。如果进行内存释放的线程是该堆的拥有者,则其释放的内存会加入到对应页的Local Free List中,而由于还可能有其他的线程来释放这些内存,因此mimalloc第三次进行Free List的Sharding,将Local Free List分为Local Free List与Thread Free List。在进行内存的释放时,如果释放的线程为内存块对应堆的拥有着则将其加入Local Free List,否则利用CAS操作将其加入Thread Free List中。mimalloc通过这次分割来保证堆的所有者线程在自己的堆上进行内存的释放是无锁的,从而提升一些性能上的表现。

Full List

第四次的Free List的Sharding其实来自于mimalloc自身的实现,其内存分配的伪代码如下。由于在mimalloc中每个堆中都有一个数组pages,该数组中每个元素都是一个由相应大小的页组成的队列;同时还有一个pages_direct的数组,该数组中每个元素对应一个内存块的大小类型,每个元素均为指向负责对应大小内存块分配的页的指针。因此mimalloc在进行内存分配时会首先从该数组指向的页中尝试进行分配,如果分配失败则调用malloc_generic,在该函数中会遍历pages数组中对应大小的队列,此时如果对应的队列中有很多页均是满的,且队列很长那么每次分配的时候都会进行队列的遍历,导致性能的损失。
void* malloc_small( size_t n ) {
heap_t* heap = tlb;
page_t* page = heap->pages_direct[(n+)>>];
block_t* block = page->free;
if (block==NULL) return malloc_generic(heap,n);
page->free = block->next;
page->used++;
return block;
}
因此mimalloc构建了一个Full List,将所有已经没有空闲空间的页放入该队列中,仅当该页中有一些空闲空间被释放后才会将其放回pages对应的队列中。而在由于内存的释放可能由对应堆的拥有者线程进行也可能由其他线程进行,因此需要一定的方式提醒对应的堆该页已经有空闲块了,同时为了避免使用锁导致的开销,mimalloc通过加入一个Thread Delayed Free List,如果一个页处于Full List中,那么在释放时会将内存块加入Thread Delayed Free List中,该队列会在调用malloc_generic时进行检测与清除(由于时Thread Local的堆,因此仅可能是拥有者来进行),因此此时仅需通过原子操作即可完成。那么还有一个问题是当释放内存的时候,其他线程如何知道是将内存块加入Thread Free List中还是Thread Delayed Free List中。mimalloc通过设置NORMAL、DELAYED、DELAYING三种状态来完成该操作。

总结

mimalloc通过将Free List进行分割,保证分配的内存具有较好的局部性并避免了锁的使用,从而获得了更好的性能。文章如果有哪里有问题,欢迎提出,对该项目感兴趣的可以去看一下其仓库1,或者参考这篇文章2

引用

mimalloc剖析的更多相关文章

  1. mimalloc内存分配代码分析

    这篇文章中我们会介绍一下mimalloc的实现,其中可能涉及上一篇文章提到的内容,如果不了解的可以先看下这篇mimalloc剖析.首先我们需要了解的是其整体结构,mimalloc的结构如下图所示   ...

  2. 探索C#之6.0语法糖剖析

    阅读目录: 自动属性默认初始化 自动只读属性默认初始化 表达式为主体的函数 表达式为主体的属性(赋值) 静态类导入 Null条件运算符 字符串格式化 索引初始化 异常过滤器when catch和fin ...

  3. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  4. [C#] 剖析 AssemblyInfo.cs - 了解常用的特性 Attribute

    剖析 AssemblyInfo.cs - 了解常用的特性 Attribute [博主]反骨仔 [原文]http://www.cnblogs.com/liqingwen/p/5944391.html 序 ...

  5. Membership三步曲之进阶篇 - 深入剖析Provider Model

    Membership 三步曲之进阶篇 - 深入剖析Provider Model 本文的目标是让每一个人都知道Provider Model 是什么,并且能灵活的在自己的项目中使用它. Membershi ...

  6. 《AngularJS深度剖析与最佳实践》简介

    由于年末将至,前阵子一直忙于工作的事务,不得已暂停了微信订阅号的更新,我将会在后续的时间里尽快的继续为大家推送更多的博文.毕竟一个人的力量微薄,精力有限,希望大家能理解,仍然能一如既往的关注和支持sh ...

  7. 探索c#之Async、Await剖析

    阅读目录: 基本介绍 基本原理剖析 内部实现剖析 重点注意的地方 总结 基本介绍 Async.Await是net4.x新增的异步编程方式,其目的是为了简化异步程序编写,和之前APM方式简单对比如下. ...

  8. ASP.NET Core管道深度剖析(2):创建一个“迷你版”的管道来模拟真实管道请求处理流程

    从<ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求>我们知道ASP.NET Core请求处理管道由一个服务器和一组有序的中间件组成,所以从总体设计来讲是非常简单的,但 ...

  9. [C#] 走进异步编程的世界 - 剖析异步方法(上)

    走进异步编程的世界 - 剖析异步方法(上) 序 这是上篇<走进异步编程的世界 - 开始接触 async/await 异步编程>(入门)的第二章内容,主要是与大家共同深入探讨下异步方法. 本 ...

随机推荐

  1. 客户端技术的一点思考(数据存储用SQLite, XMPP通讯用Gloox, Web交互用LibCurl, 数据打包用Protocol Buffer, socket通讯用boost asio)

    今天看到CSDN上这么一篇< 彻底放弃没落的MFC,对新人的忠告!>, 作为一个一直在Windows上搞客户端开发的C++程序员,几年前也有过类似的隐忧(参见 落伍的感觉), 现在却有一些 ...

  2. UBUTUN 通过蓝牙连接Hoary和诺基亚手机

    通过蓝牙连接Hoary和诺基亚手机 这个how to已经用Hoary.诺基亚6630和一个道尔芯片(Dongle)蓝牙(Usb蓝牙)测试过了.通过这个How to,你可以:-通过蓝牙,从你的电脑发送文 ...

  3. Delphi 7下最小化到系统托盘(主要是WM_TRAYMSG和WM_SYSCOMMAND消息)

    在Delphi 7下要制作系统托盘,只能制作一个比较简单的系统托盘,因为ShellAPI文件定义的TNotifyIconData结构体是比较早的版本.定义如下: 123456789   _NOTIFY ...

  4. Win8 Metro(C#)数字图像处理--3.1图像均值计算

    原文:Win8 Metro(C#)数字图像处理--3.1图像均值计算 /// <summary> /// Mean value computing. /// </summary> ...

  5. android x86 7.0 32bit调试apk时出现的错误

    detected problems with app native libraries libavcodec.so:text relocationslibavutil.solibswresample. ...

  6. Resources.resx 未将对象引用设置到对象的实例

    原文:解决使用DevExpress开发错误:未将对象引用设置到对象的实例 在使用DevExpress是总是会出现一些状况.这次同事在他的机器上调试完毕的代码发过来,却出现“未将对象引用设置到对象的实例 ...

  7. 二叉树基本操作C代码

    #include<stdio.h> #include<malloc.h> #define LEN sizeof(struct ChainTree) struct ChainTr ...

  8. WPF四年,尤不足以替代WinForm

    WPF四年,尤不足以替代WinForm WPF出山已四年,作为官方内定的下一代UI系统掌门,没少露脸.但这个新掌门能否胜任,仍是众多开发者的心头之虑.通过对VisualStudio 2010的编辑器部 ...

  9. Qt浅谈之二:钟表(时分秒针)

    一.简介 QT编写的模拟时钟,demo里的时钟只有时针和分针,在其基础上添加了秒针,构成了一个完整的时钟.能对2D绘图中坐标系统.平移变换(translate).比例变换(scale).旋转变换(ro ...

  10. Eric Linux - 1 Basic concepts of linux

    Computer basic Computer 5 parts CPU Input Output Memory External storage device. CPU RISC: Reduced I ...