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. delphi dom动态创建节点方法

    在一次测试demo中 需要动态的创建xml节点并添加,实现方法如下: var NewItem:IXMLDOMElement; NewItem:=ConfigDoc.createElement('ite ...

  2. 超详细SQL SERVER 2016跨网段和局域网发布订阅配置图解和常见问题

    原文:超详细SQL SERVER 2016跨网段和局域网发布订阅配置图解和常见问题 转载标明出处:http://blog.csdn.net/u012861467 前方高能,要有点耐心,图片较多,注意在 ...

  3. C# 屏蔽Ctrl Alt Del 快捷键方法+屏蔽所有输入

    原文:C# 屏蔽Ctrl Alt Del 快捷键方法+屏蔽所有输入 Win32.cs /* * * FileCreate By Bluefire * Used To Import WindowsApi ...

  4. Windows的远程协助和远程桌面的区别

    在Windows的“系统属性-远程”里面,包含了“远程协助”和“远程桌面”两个设置. 远程桌面我们平时用得比较多,但是远程协助却一直没明白什么作用.系统装完以后,“远程协助 - 允许远程协助连接这台计 ...

  5. 数据库连接池之_DButils

    // 这个是在添加数据 @Test public void demo1() { QueryRunner qr = new QueryRunner(JDBCUtils.getDataSource()); ...

  6. 把滚动箱的样式做如下调整来模拟 TPanel

    程序中用 TPanel 做了容器, 需要给它一个背景图片; 发现这竟是个难题! 发现我经常使用的滚动箱控件 TScrollBox, 是一个很好的替代品. 本例需要先添加两个图片资源, 添加方法可以参考 ...

  7. C# Task 的用法

    C# Task 的用法(转自:http://www.wxzzz.com/683.html#) 其实Task跟线程池ThreadPool的功能类似,不过写起来更为简单,直观.代码更简洁了,使用Task来 ...

  8. .NET Core 微服务之Polly熔断策略

    紧接着上一篇说,咱们继续介绍Polly这个类库 熔断策略(Circuit-breaker) 如果调用某个目标服务出现过多超时.异常等情况,可以采取一定时间内熔断该服务的调用,熔断期间的请求将不再继续调 ...

  9. Spark之常用操作

    -- 筛选 val rdd = sc.parallelize(List("ABC","BCD","DEF")) val filtered = ...

  10. 用composer安装php代码(以安装phpmailer为例)

    1.安装composer.exe软件 2.下载composer.phar 3.创建composer.json文件 { "require": { "php": & ...