<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.8.5">Jekyll</generator><link href="https://uncp.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://uncp.github.io/" rel="alternate" type="text/html" /><updated>2019-05-11T08:12:05+00:00</updated><id>https://uncp.github.io/feed.xml</id><title type="html">UncP’s Blog</title><subtitle></subtitle><author><name>UncP</name><email>uncp.xu@gmail.com</email></author><entry><title type="html">Adaptive Radix Tree</title><link href="https://uncp.github.io/Adaptive-Radix-Tree/" rel="alternate" type="text/html" title="Adaptive Radix Tree" /><published>2019-05-02T00:00:00+00:00</published><updated>2019-05-02T00:00:00+00:00</updated><id>https://uncp.github.io/Adaptive-Radix-Tree</id><content type="html" xml:base="https://uncp.github.io/Adaptive-Radix-Tree/">&lt;p&gt;这篇文章介绍 &lt;strong&gt;Adaptive Radix Tree&lt;/strong&gt; 并且提供其并发算法 &lt;strong&gt;Multi-ART&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;论文链接：&lt;a href=&quot;https://db.in.tum.de/~leis/papers/ART.pdf&quot;&gt;The Adaptive Radix Tree: ARTful Indexing for Main-Memory Databases&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;adaptive-radix-tree&quot;&gt;Adaptive Radix Tree&lt;/h3&gt;

&lt;p&gt;Adaptive Radix Tree(ART) 中文名字是可变基数树，相较于传统的 radix tree，其最大的区别在于每个节点可以容纳的 key 是动态变化的，这样既可以节省空间，又可以提高缓存局部性。&lt;/p&gt;

&lt;p&gt;art 内部节点分为4种类型，分别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;node4&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;node16&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;node48&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;node256&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;node4&quot;&gt;Node4&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/art_node4.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Node4&lt;/code&gt; 有4个槽，存储4个 &lt;code class=&quot;highlighter-rouge&quot;&gt;unsigned char&lt;/code&gt; 和4个指针，这些指针可以指向叶子节点(即 key 指针)，也可以指向下一层内部节点。当 &lt;code class=&quot;highlighter-rouge&quot;&gt;Node4&lt;/code&gt; 存放第5个 key byte 时需要将其扩大为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Node16&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;node16&quot;&gt;Node16&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/art_node16.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Node16&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Node4&lt;/code&gt; 在结构上是一致的，但是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Node16&lt;/code&gt; 可以存放16个 &lt;code class=&quot;highlighter-rouge&quot;&gt;unsigned char&lt;/code&gt; 和16个指针。当存放第17个 key byte 时需要将其扩大为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Node48&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;node48&quot;&gt;Node48&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/art_node48.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Node48&lt;/code&gt; 结构上和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Node4&lt;/code&gt;, &lt;code class=&quot;highlighter-rouge&quot;&gt;Node16&lt;/code&gt; 有所不同，它有256个索引槽和48个指针，这256个索引槽对应 &lt;code class=&quot;highlighter-rouge&quot;&gt;unsigned char&lt;/code&gt; 的 0-255，然后每个索引槽的值对应指针的位置，分别为 1-48，如果某个字节不存在的话，那么它的索引槽的值就是0。当存放第49个 key byte 时需要将其扩大为 &lt;code class=&quot;highlighter-rouge&quot;&gt;Node256&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;node256&quot;&gt;Node256&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/art_node256.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Node256&lt;/code&gt; 直接存放256个指针，每个指针对应 &lt;code class=&quot;highlighter-rouge&quot;&gt;unsigned char&lt;/code&gt; 的 0-255 区间。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;除了以上的可变节点，ART 还引入了两种技术来进一步减少内存占用（尤其是在 long key 情况下），分别是 &lt;code class=&quot;highlighter-rouge&quot;&gt;Path Compression&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;Lazy Expansion&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;path-compression--lazy-expansion&quot;&gt;Path Compression &amp;amp; Lazy Expansion&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/art_compression.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Lazy Expansion 用于区别两个叶子节点时才进行创建内部节点，比如上图中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;FOO&lt;/code&gt;，为了节省空间，两个内部节点不会被创建，只会存在一个叶子节点，当另一个 key 比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;FPO&lt;/code&gt; 被插入时，才会创建一个内部节点以区别 &lt;code class=&quot;highlighter-rouge&quot;&gt;O&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;P&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;Path Compression 用于移除只有单个子节点的节点，比如上图中带有 &lt;code class=&quot;highlighter-rouge&quot;&gt;A&lt;/code&gt; 的节点会被合并入父节点。&lt;/p&gt;

&lt;p&gt;节点合并带来了前缀，前缀需要在下降时进行比较，所以产生了两种方法，一种是悲观方法，即每个节点专门开辟一个变长区间存放前缀，每次下降时需要进行比较；另一种是乐观方法，只存储前缀的长度，下降时跳过这个长度，然后到达叶子节点时再回头利用叶子节点进行前缀的比较。在 ART 的实现中结合了这两种方法，每个节点存放最多8个字节的前缀，下降会根据前缀长度进行动态切换。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;查找算法&quot;&gt;查找算法&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/art_search.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;查找流程很简单：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果当期节点为空代表不存在；&lt;/li&gt;
  &lt;li&gt;如果当前节点是叶子节点并且 key 相等那么即代表找到；&lt;/li&gt;
  &lt;li&gt;如果当前节点是内部节点需要首先比较前缀，前缀不同则表明 key 不存在，相同则可以继续进行下降。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;插入算法&quot;&gt;插入算法&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/art_insert.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;插入流程大致分为以下几步：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;如果下降到的节点是空节点，那么用叶子节点替换掉这个叶子节点；&lt;/li&gt;
  &lt;li&gt;如果下降到的节点是叶子节点，那么需要进行比较，如果 key 存在退出；否则使用新的内部节点替换到当前叶子节点，同时需要根据两个 key 获取前缀&lt;/li&gt;
  &lt;li&gt;如果下降到的是内部节点，需要首先比较前缀，如果前缀不符合的话，生成新节点，令公共前缀为其前缀，公共前缀后一字节作为区分两个 key 的字节，然后将叶子节点和截断公共前缀后的老节点插入到这个新节点中&lt;/li&gt;
  &lt;li&gt;如果下降到的是内部结点并且前缀相等，如果存在下一层节点的话，继续进行下降；否则直接将叶子节点插入，并根据需要进行节点大小的调整&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;删除算法&quot;&gt;删除算法&lt;/h4&gt;

&lt;p&gt;参考插入算法。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;multi-art&quot;&gt;Multi-ART&lt;/h3&gt;

&lt;p&gt;Multi-ART 是 ART 的并发算法，是我实习时开始琢磨的，到现在断断续续地有三个月了。Multi-ART 参考了 &lt;a href=&quot;https://zhuanlan.zhihu.com/p/52624601&quot;&gt;Mass Tree&lt;/a&gt; 的并发策略即 &lt;code class=&quot;highlighter-rouge&quot;&gt;lock-free read&lt;/code&gt; + &lt;code class=&quot;highlighter-rouge&quot;&gt;fine-grained-locking write&lt;/code&gt;，然后根据 ART 的特点做出了调整。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;设计&quot;&gt;设计&lt;/h4&gt;

&lt;p&gt;在实现完 Mass Tree 之后决定自己设计一个并发算法，于是选择了 ART。ART 是很久之前就知道的一种索引，当时将某个 C 实现的 ART 翻译成了 C++ 实现，然后非常惊讶于其性能，所以选择了 ART 来设计并发算法。&lt;/p&gt;

&lt;p&gt;整个设计中最核心的问题就是&lt;strong&gt;“如何正确下降到下一层节点”&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;在并发 B-Tree（包括 Mass Tree）中，下降最大的障碍在于分裂，下降前根据 key 判断应该下降到 A 节点，但实际上由于节点的横向分裂实际需要下降到另一个节点 B，但是在 Multi-ART 中，情况又有很大的不同，ART 节点不存在横向分裂的行为，取而代之的是：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;节点竖向分裂或合并，由节点前缀变更引起&lt;/li&gt;
  &lt;li&gt;节点原地扩展或收缩，由插入和删除引起&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/art_node_split.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;对于 B Tree 或者 Mass Tree 来说，为了保证下降到正确的节点，有两种机制，第一种是每个节点自带一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;next&lt;/code&gt; 域和一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;sentinel key&lt;/code&gt;，用于进行节点之间的右移，比如 B Link Tree；第二种是重试，即从某个子树进行重新下降，比如 Mass Tree。&lt;/p&gt;

&lt;p&gt;对于 Multi-ART 来说，我们首先考虑&lt;strong&gt;节点的原地扩展或收缩&lt;/strong&gt;，即上图中 &lt;code class=&quot;highlighter-rouge&quot;&gt;ART Node Expand&lt;/code&gt; 这种情况。为了保证正确地下降，对每个节点引入 &lt;code class=&quot;highlighter-rouge&quot;&gt;old&lt;/code&gt; 这个域（只需占用某个 bit），每次节点发生扩展或收缩时需要将当前节点标记为 &lt;code class=&quot;highlighter-rouge&quot;&gt;old&lt;/code&gt;。如果在下降到这个节点时发现这个节点已经处于 &lt;code class=&quot;highlighter-rouge&quot;&gt;old&lt;/code&gt; 状态，即代表它已经被一个新的节点取代了，那么为了获取正确的节点，这里可以有两种辅助机制：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;每个节点引入 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 指针，如果当前节点被替换了，通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;new&lt;/code&gt; 来获取新节点&lt;/li&gt;
  &lt;li&gt;根据父节点来获取新节点，因为父节点中旧的节点会被原地替换为新节点&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;以上两种机制都能正常工作，考虑到 ART 原有算法，我采用了 &lt;code class=&quot;highlighter-rouge&quot;&gt;old&lt;/code&gt; 域 + 通过父节点获取新节点这种方式。&lt;/p&gt;

&lt;p&gt;最后我们考虑&lt;strong&gt;节点的竖向分裂或合并&lt;/strong&gt;，即上图中 &lt;code class=&quot;highlighter-rouge&quot;&gt;ART Node Split&lt;/code&gt; 这种情况。这里需要两个机制来保证正确性。&lt;/p&gt;

&lt;p&gt;一是对每个节点引入 &lt;code class=&quot;highlighter-rouge&quot;&gt;offset&lt;/code&gt; 域，即如果下降到这个节点，该从哪个偏移开始比较。比如某个节点前缀是 &lt;code class=&quot;highlighter-rouge&quot;&gt;ABC&lt;/code&gt;，节点的 &lt;code class=&quot;highlighter-rouge&quot;&gt;offset&lt;/code&gt; 是4，如果在另一个线程下降过程中发生了竖向分裂，那么可能前缀变成了 &lt;code class=&quot;highlighter-rouge&quot;&gt;BC&lt;/code&gt;，&lt;code class=&quot;highlighter-rouge&quot;&gt;offset&lt;/code&gt; 变成了 5，那么另一个线程就不能用 &lt;code class=&quot;highlighter-rouge&quot;&gt;offset 4&lt;/code&gt; 进行比较，当它发现 &lt;code class=&quot;highlighter-rouge&quot;&gt;offset 5&lt;/code&gt; 时，即知道发生了前缀变化，需要进行重试。&lt;/p&gt;

&lt;p&gt;二是对每个节点引入 &lt;code class=&quot;highlighter-rouge&quot;&gt;expand version&lt;/code&gt; 域，比较节点前缀前后都需要获取这个域来保证节点的前缀在比较时没有发生改变。&lt;/p&gt;

&lt;p&gt;三是对每个节点引入 &lt;code class=&quot;highlighter-rouge&quot;&gt;parent&lt;/code&gt; 域，因为两个线程可能一前一后更改了前缀，所以第二个线程替换父节点中的节点时应该获取上图中的绿色节点，而不是下降时的红色节点。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;为了支持这个算法，对每个 ART 节点引入了 &lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt;(8字节) 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;parent&lt;/code&gt;(8字节) 域。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/art_node_version.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;div class=&quot;language-markdown highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;off: 节点的 offset
count: 节点的 key 数量
prefix_len: 前缀长度
type: node4 | node16 | node48 | node256
old: 节点是否是旧的
lock: 用于写线程加锁
expand: 是否正在发生前缀变化
vexpand: 前缀的 version
insert 和 vinsert 目前没有使用
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;以上就是 Multi-ART 的设计核心问题，其实花的精力远远不止以上这点篇幅，还有很多其它的设计细节和实现细节，懒得展开了，可以参考这个专栏之前的相关并发算法的文章。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;性能&quot;&gt;性能&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/art_benchmark.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;预计在55个线程左右 Multi-ART 8字节每秒随机写入可以破亿。&lt;/p&gt;

&lt;p&gt;我的 GitHub 上有这四个算法的实现，这个知乎专栏也有这个四个算法的介绍。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;Multi-ART 的性能相较于 B Tree based 的并发算法，几乎是碾压。（当然这里需要指出的是为了实现的简单，并不保证 Multi-ART 的 Node4, Node16 和 Node48 中 key 是有序的）。&lt;/p&gt;

&lt;p&gt;Multi-ART 的高性能有很多因素，比如算法时间复杂度低，cache 友好的节点设计，良好的并发策略设计等等。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;总结&quot;&gt;总结&lt;/h3&gt;

&lt;p&gt;这篇文章介绍了 ART 以及 Multi-ART 的设计与实现。&lt;/p&gt;

&lt;p&gt;GitHub 地址：&lt;a href=&quot;https://github.com/UncP/aili&quot;&gt;UncP/aili&lt;/a&gt;。&lt;/p&gt;</content><author><name>UncP</name><email>uncp.xu@gmail.com</email></author><summary type="html">这篇文章介绍 Adaptive Radix Tree 并且提供其并发算法 Multi-ART。</summary></entry><entry><title type="html">4 Modifications For Raft Consensus</title><link href="https://uncp.github.io/4-Modifications-for-Raft-Consensus/" rel="alternate" type="text/html" title="4 Modifications For Raft Consensus" /><published>2019-01-26T00:00:00+00:00</published><updated>2019-01-26T00:00:00+00:00</updated><id>https://uncp.github.io/4-Modifications-for-Raft-Consensus</id><content type="html" xml:base="https://uncp.github.io/4-Modifications-for-Raft-Consensus/">&lt;h2 id=&quot;全局唯一的数据库-id&quot;&gt;全局唯一的数据库 ID&lt;/h2&gt;

&lt;h3 id=&quot;作用&quot;&gt;作用&lt;/h3&gt;

&lt;p&gt;防止其他集群的成员加入到当前集群中&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id=&quot;pre-vote&quot;&gt;Pre-Vote&lt;/h2&gt;

&lt;h3 id=&quot;作用-1&quot;&gt;作用&lt;/h3&gt;

&lt;p&gt;防止“term inflation”（即一个存在网络问题的机器发起选举，但是没有收到回应，导致 term 一直增长，直到很长一段时间之后才成功当选 leader）。&lt;/p&gt;

&lt;h3 id=&quot;行为&quot;&gt;行为&lt;/h3&gt;

&lt;p&gt;引入 pre-vote 算法，在成员转变为 &lt;code class=&quot;highlighter-rouge&quot;&gt;candidate&lt;/code&gt; 之前会先发起一次 pre-vote rpc，当这个 rpc 收到多数成员的同意之后才会发起真正的选举。&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Pre-Vote RPC

arguments:
nextTerm: caller's term + 1
candidateId: caller
lastLogIndex
lastLogTerm

results:
term: currentTerm, for caller to update itself
voteGranted: true means caller would receive vote if it was a candidate

receiver:
1. reply false if last AppendEntries call was received less than election timeout ago(leader stickness)
2. reply false if nextTerm &amp;lt; currentTerm
3. if caller's log is at least as up-to-date as receiver's log, return true
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;另外，对于 pre-vote 来说，不存在同一任期一个成员只能投一次票这样的限制，因为到达真正的投票阶段时，只会有一个 leader。&lt;/p&gt;

&lt;h2 id=&quot;leader-stickness&quot;&gt;Leader stickness&lt;/h2&gt;

&lt;h3 id=&quot;作用-2&quot;&gt;作用&lt;/h3&gt;

&lt;p&gt;防止 leader 的频繁切换。对于 raft 来说这是正确的行为，但是对于应用来说，如果需要频繁的切换访问 leader 会带来很大的问题。&lt;/p&gt;

&lt;h3 id=&quot;行为-1&quot;&gt;行为&lt;/h3&gt;

&lt;p&gt;收到投票时，在一个 election timeout 之内当前成员曾收到过 AppendEntry RPC，那么它会拒绝这次投票。&lt;/p&gt;

&lt;p&gt;这种行为的引入使得只有当超过半数的成员在 election timeout 之外没有收到 AppendEntry 后，选举才可能成功。其实这样的行为和 redis cluster 中的 master 切换是比较相似的。&lt;/p&gt;</content><author><name>UncP</name><email>uncp.xu@gmail.com</email></author><summary type="html">全局唯一的数据库 ID</summary></entry><entry><title type="html">Mass Tree</title><link href="https://uncp.github.io/Mass-Tree/" rel="alternate" type="text/html" title="Mass Tree" /><published>2018-12-13T00:00:00+00:00</published><updated>2018-12-13T00:00:00+00:00</updated><id>https://uncp.github.io/Mass-Tree</id><content type="html" xml:base="https://uncp.github.io/Mass-Tree/">&lt;p&gt;这篇文章介绍 Trie 树和 B+ 树结合而成的并发算法——&lt;strong&gt;Mass Tree&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt; Mass Tree 是 2012 年提出的，引用了很多 OLFIT Tree 以及 B&lt;sup&gt;link&lt;/sup&gt; Tree 的概念。（这个专栏的第一篇文章介绍了 B&lt;sup&gt;link&lt;/sup&gt; Tree。）&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;论文链接：&lt;a href=&quot;https://pdos.csail.mit.edu/papers/masstree:eurosys12.pdf&quot;&gt;Cache Craftiness for Fast Multicore Key-Value Storage&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;mass-tree-结构&quot;&gt;Mass Tree 结构&lt;/h3&gt;

&lt;p&gt;从结构上来说，Mass Tree 是由一层或多层 B+ 树组成的 Trie 树。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/mass_tree_structure.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;上图中，&lt;em&gt;圆形&lt;/em&gt;代表内部节点（interior node，也就是 B+ 树的 branch node），&lt;em&gt;矩形&lt;/em&gt;代表边缘节点（border node，也就是 B+ 树的 leaf node），&lt;em&gt;五角星&lt;/em&gt;代表 value。border node 的 value 域可能存放的是数据，也可能存放的是下一层子树的根节点。&lt;/p&gt;

&lt;p&gt;每个虚线框代表一棵 B+ 树，对于每一层（Layer）的 B+ 树，采用8字节进行索引。长度小于 (8h + 8) 字节的 key 会被存放在 layer &amp;lt;=h。比如，两个 key 存放在 layer 1，那么它们的前8字节是相同的。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;注意上面 &amp;lt;= h，极端情况下才会放在 h 层，否则会尽可能地把 key 放在较低的层，比如 Mass Tree 中所有 key 都是8字节，那么它们都位于 layer 0，突然来了一个1k字节长度的 key，显然没有必要把这个 key 放在第一百多层，如果这个 key 的前8字节没有和其他 key 冲突的话，那么它也会被放在 layer 0，如果发生了冲突，那么我们只需要生成新的子树（subtree），把这个 key 放在 layer 1 就可以，即层数是懒惰生成的，只有当出现节点8字节索引相同时才会生成新的 layer。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;同一层的所有叶节点会被双向连接（&lt;code class=&quot;highlighter-rouge&quot;&gt;next&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;prev&lt;/code&gt;）在一起，但是内部结点是不连接的，这和 B&lt;sup&gt;link&lt;/sup&gt; Tree 不一样，后者对所有节点维护单向连接（&lt;code class=&quot;highlighter-rouge&quot;&gt;next&lt;/code&gt;），所以 Mass Tree 在进行根节点到叶子节点的下降过程时，逻辑和  B&lt;sup&gt;link&lt;/sup&gt; Tree 是不一样的。&lt;/p&gt;

&lt;p&gt;B&lt;sup&gt;link&lt;/sup&gt; Tree 的每一个 border node 都带有一个 higher key，用于判断是否需要进行节点右移，但是 Mass Tree 没有 higher key，所以判断是否右移需要获取下一个节点。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;对于叶节点来说，higher key 并不是必须的。请问这是为什么？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mass Tree 给出了完整的内部节点和叶节点结构，这里只介绍叶节点的原子状态跃迁，为了防止读线程读到中间状态，叶节点被设计成最多存放 15 个 key，引入了一个8字节64位的 &lt;code class=&quot;highlighter-rouge&quot;&gt;permutation&lt;/code&gt;(uint64_t)，这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;permutation&lt;/code&gt; 被划分成16份，每份4位，其中1份代表当前节点的 key 数量，另外15份用于存放每个 key 在节点中实际位置的索引，key 的插入是顺序插入，之后只需要修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;permutation&lt;/code&gt; 来更新节点内 key 的索引信息，然后施加一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;release&lt;/code&gt; 语义，当读线程对这个节点的 &lt;code class=&quot;highlighter-rouge&quot;&gt;permutation&lt;/code&gt; 施加 &lt;code class=&quot;highlighter-rouge&quot;&gt;acquire&lt;/code&gt; 语义时，可以获取到完整的节点信息。&lt;/p&gt;

&lt;p&gt;  &lt;/p&gt;

&lt;h3 id=&quot;mass-tree-算法&quot;&gt;Mass Tree 算法&lt;/h3&gt;

&lt;h4 id=&quot;并发策略&quot;&gt;并发策略&lt;/h4&gt;

&lt;p&gt;Mass Tree 的并发策略和 OLFIT Tree 是相似的：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;fine-grained locking，即节点锁，解决 write-write 竞争，同一时刻只有一个线程可以对当前节点进行写操作&lt;/li&gt;
  &lt;li&gt;optimistic concurrency control，即节点 &lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt; (uint32_t)，解决 read-write 竞争，读开始前和读结束后都需要获取当前节点的最新 version，来判断在读过程中当前节点是否发生了写操作（插入或分裂），同时对节点的写操作都需要先修改 version，在插入 key 之前需要设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;inserting&lt;/code&gt; 标记，插入完成之后将 insert 的 vinsert + 1；在分裂之前需要设置 &lt;code class=&quot;highlighter-rouge&quot;&gt;splitting&lt;/code&gt; 标记，分裂完成之后将 split 的 vsplit + 1。&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/mass_tree_version.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;和 &lt;code class=&quot;highlighter-rouge&quot;&gt;permutation&lt;/code&gt; 一样，&lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt; 也被划分成了多个域，除了带有 insert 和 split 信息之外，还包含一个 lock bit，用于加锁；一个 root bit，用于指示否是根节点；一个 border bit，用于指示是否是 border node；一位 deleted 位，用于指示这个节点是否被标记为删除。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;下降流程&quot;&gt;下降流程&lt;/h4&gt;

&lt;p&gt;对于每次插入，获取，删除操作，都需要从 root（可能是整棵树的根节点，也可能是某棵子树的根节点） 下降到对应的叶节点（border node）。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/mass_tree_find_border_node.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;首先，在开始读取节点之前，必须获得节点的 stable version（图中橙色方块），即 &lt;code class=&quot;highlighter-rouge&quot;&gt;version&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;inserting&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;splitting&lt;/code&gt; 位都为0。&lt;/p&gt;

&lt;p&gt;其次，在下降之前，需要获取最新的 root，因为在开始下降前，根节点可能分裂了，导致其发生了改变（图中蓝色方块）。&lt;/p&gt;

&lt;p&gt;最后，如果当前节点已经是叶节点，那么可以返回，否则需要进行下降，读取内部结点根据 &lt;code class=&quot;highlighter-rouge&quot;&gt;key[x, x+8)&lt;/code&gt;(8字节) 获得下降节点之后，分为3种情况处理：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;case 1 那行伪代码可以这样用 C 语言表示：&lt;/p&gt;

    &lt;pre&gt;&lt;code class=&quot;language-C&quot;&gt;uint32_t before = node_get_stable_version(n);
// read node here
uint32_t after = node_get_version(n); // no need to be stable, just latest version
if ((before ^ after == LOCK_BIT) || (before ^ after == 0))
    // neither insert nor split happened
&lt;/code&gt;&lt;/pre&gt;

    &lt;p&gt;节点在我们读取期间没有发生任何变化，我们可以安全地进行下降；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;节点发生了变化，而且是分裂，那么我们需要从根节点重新进行下降（内部节点没有相互连接起来，所以不能像 B&lt;sup&gt;link&lt;/sup&gt; Tree 那样获取右节点）；&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;节点发生了变化，但只是插入，只需要重新对当前节点进行下降&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;注意第二个黄色方块，这一行非常关键，如果你细看下的话，它似乎可以挪到 case 1 的 if 语句里面，但实际上不能这样做。因为如果当前节点的孩子节点发生了分裂，但是还没来得及将新节点插入到当前节点，对调这两行代码，可能会导致下降到错误的节点。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;插入流程&quot;&gt;插入流程&lt;/h4&gt;

&lt;p&gt;作者只给出了读取伪代码，这里给出插入伪代码。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/mass_tree_insert.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;当我们通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;findborder&lt;/code&gt; 下降到叶节点后，需要对其加锁（图中蓝色方块），但是此时并不能直接将 key 进行插入，当两个写线程同时下降到同一个叶节点时，只有一个线程可以进行写入，所以加锁后需要查看在加锁前是否有其他线程对这个节点进行了写入，如果有的话需要查看是否需要右移，这里的逻辑和 B&lt;sup&gt;link&lt;/sup&gt; Tree 是一样的。&lt;/p&gt;

&lt;p&gt;当我们成功加锁并且定位到正确的叶节点之后，可以进行写入，结果有4种：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;插入 key 成功或 key 已经存在；&lt;/li&gt;
  &lt;li&gt;需要下降到下一层，同时需要增加索引的偏移；&lt;/li&gt;
  &lt;li&gt;存在索引冲突，比如 “12345678AA” 和 “12345678BB”，此时需要创建一棵子树，存放 “AA” 和 “BB”，同时需要把原来存放 “AA” 的地方替换成这棵子树的根节点&lt;/li&gt;
  &lt;li&gt;节点已满，需要分裂之后再次插入（见下一小节）&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;橙色方块在下面删除流程中介绍&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;分裂流程&quot;&gt;分裂流程&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/mass_tree_split.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;分裂流程和 B&lt;sup&gt;link&lt;/sup&gt; Tree 是一样的逻辑，需要同时锁住当前节点以及父节点，分为三种情况：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;当前节点没有父节点，即当前节点是某个子树的根节点或者整棵树的根节点，在这种情况下我们需要生成新节点作为根节点。有个特殊的地方需要处理，对于子树来说，它的节点存放在上层的叶节点中，但是上图中并没有提到当子树分裂时，如何将上层叶节点中旧的根节点替换掉，这里有两种处理方法：
    &lt;ul&gt;
      &lt;li&gt;懒惰处理，不设置根节点的父节点，但是会在写路径中添加判断，当从叶节点下降到某个子树的根节点时，会判断其是否是真正的根节点，如果不是的话，会追溯到新的父节点，然后在叶节点替换掉旧根节点（这种懒惰处理的思想很巧妙）；&lt;/li&gt;
      &lt;li&gt;设置子树的根节点的父节点为上一层的叶节点，只需要在 case1 和 case2 中添加一种 case，处理子树根节点的替换（这两种方法没有优劣之分）&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;父节点没有满，则只需要将 fence key 和新节点插入到父节点&lt;/li&gt;
  &lt;li&gt;父节点已满，需要分裂父节点然后进行插入，之后需要将分裂的新节点提升到更上层&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;Mass Tree 每个节点都有一个指向父节点的指针，B&lt;sup&gt;link&lt;/sup&gt; Tree 的节点可以不需要父节点，请问这是为什么？&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;读取流程&quot;&gt;读取流程&lt;/h4&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/mass_tree_get.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;整体而言和插入流程是一样的，这里不再展开，有个点需要注意：&lt;/p&gt;

&lt;p&gt;unstable 状态（图中绿色方块），插入流程的 case 3，当出现8字节索引相同时，需要生成子树来替换掉这个 value，但是这个操作不会修改 &lt;code class=&quot;highlighter-rouge&quot;&gt;inserting&lt;/code&gt; 或者 &lt;code class=&quot;highlighter-rouge&quot;&gt;splitting&lt;/code&gt; 标记，所以需要先标记这个 slot 为 unstable，然后把 value 替换为子树根节点的指针（以及一些其他操作），来防止读到不稳定状态（说实话，这个状态的引入似乎增加了复杂性，其实可以在替换之前标记 &lt;code class=&quot;highlighter-rouge&quot;&gt;inserting&lt;/code&gt;，这样也会触发重试）；&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;橙色方块，在下面删除流程中介绍&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;删除流程&quot;&gt;删除流程&lt;/h4&gt;

&lt;p&gt;这里只讨论&lt;strong&gt;逻辑删除&lt;/strong&gt;，&lt;strong&gt;物理删除&lt;/strong&gt;需要额外的技术，比如 hazard pointer，epoch-based reclamation 等等。&lt;/p&gt;

&lt;p&gt;逻辑删除和B+树的类似，但是我们并不对 key 少的节点进行合并，当节点 key 减少到0时，需要标记这个节点为 &lt;code class=&quot;highlighter-rouge&quot;&gt;deleted&lt;/code&gt;，然后将其从父节点删除，同时如果是叶节点的话，还需要维护叶节点的双向连接（这是为什么叶节点需要双向连接的一大原因，维护起来简单）。如果某棵子树为空的话也可以删除整棵子树。当其他线程发现节点处于 &lt;code class=&quot;highlighter-rouge&quot;&gt;deleted&lt;/code&gt; 状态时，需要进行重试，因为这个节点逻辑上是不存在的。&lt;/p&gt;

&lt;p&gt;除此以外还有一种特殊情况需要考虑：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/mass_tree_delete.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;左边的线程根据 k1 定位到了位置 i，在读取 v1 之前这个节点发生了删除位于位置 i 的 k1，同时在位置 j 处插入 k2，如果 i 等于 j，可能导致左边的线程读取到 v2，为了解决这个问题，需要在索引 i 被删除后重新利用时增加节点的 &lt;code class=&quot;highlighter-rouge&quot;&gt;vinsert&lt;/code&gt; 域。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;以上就是 Mass Tree 算法的整体介绍。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;mass-tree-优化&quot;&gt;Mass Tree 优化&lt;/h3&gt;

&lt;p&gt;其实节点的结构，比如每个节点存放15个 key 本身就是作者测试过后的最优解。作者还在论文里也提到了不少优化，比如节点预取，索引（&lt;code class=&quot;highlighter-rouge&quot;&gt;uint64_t&lt;/code&gt;）用整形数比较，定制的节点内存分配器，以及 key 的后缀的管理，等等。这里不做展开。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;mass-tree-总结&quot;&gt;Mass Tree 总结&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;Trie 和 B+ 两种索引树的结合，使得在下降过程中的重试不需要从整棵树的根节点开始，同时加快拥有相同前缀的 key 的处理速度&lt;/li&gt;
  &lt;li&gt;具体的内部结点与外部节点的结构&lt;/li&gt;
  &lt;li&gt;fine-grained locking 写以及 lock-free 读&lt;/li&gt;
  &lt;li&gt;比较具体的节点删除机制&lt;/li&gt;
  &lt;li&gt;较小的节点减少了线程的竞争&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
  &lt;p&gt;需要指出的是这个算法不支持性能线性扩展，不过这并不是问题。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这个算法实现起来很有&lt;strong&gt;挑战性&lt;/strong&gt;，个人认为主要有两个方面，第一个是对于&lt;strong&gt;树结构的把握&lt;/strong&gt;，Mass Tree 是 Trie 和 B+ 树的结合。第二个是对于&lt;strong&gt;并发下可见性的理解&lt;/strong&gt;，”先改哪个域后改哪个域“，”谁先可见谁后可见“，”谁何时可见“，“在哪加以及加不加 memory barrier”等等这样的问题需要花很多时间考虑，而且出现 bug 非常难调试。&lt;/p&gt;

&lt;p&gt;Mass Tree 性能比我预想中要好很多，对于均匀分布的10字节 key 随机插入，数据量1000万，在我的机器上4个线程可以到500万以上的 tps；对于分布集中的测试数据，可以到1000万以上。其实&lt;strong&gt;对于内存索引来说，cache miss 对性能造成的影响有时候是远远大于线程竞争的&lt;/strong&gt;，所以尽管数据分布非常集中（线程竞争更多），仍然可以获得更高的性能。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;这是我的 Mass Tree 实现：&lt;a href=&quot;https://github.com/UncP/aili&quot;&gt;UncP/aili&lt;/a&gt;，没有实现删除操作。&lt;/p&gt;

&lt;p&gt;这是另一个实现：&lt;a href=&quot;https://github.com/rmind/masstree&quot;&gt;https://github.com/rmind/masstree&lt;/a&gt;，代码确实漂亮。他采用了激进生成子树的策略，缺点就是 cache locality 在有些情况下不好。&lt;/p&gt;

&lt;p&gt;这是论文作者的实现：&lt;a href=&quot;https://github.com/kohler/masstree-beta&quot;&gt;https://github.com/kohler/masstree-beta&lt;/a&gt;。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;ps&quot;&gt;PS:&lt;/h4&gt;

&lt;p&gt;最后推荐一个算法，Palm Tree，这个算法很好地诠释了什么是大道至简。你可以在这个专栏之前的文章中找到其介绍。&lt;/p&gt;</content><author><name>UncP</name><email>uncp.xu@gmail.com</email></author><summary type="html">这篇文章介绍 Trie 树和 B+ 树结合而成的并发算法——Mass Tree。</summary></entry><entry><title type="html">The Art Of Multiprocessor Programming</title><link href="https://uncp.github.io/The-Art-of-Multiprocessor-Programming/" rel="alternate" type="text/html" title="The Art Of Multiprocessor Programming" /><published>2018-11-19T00:00:00+00:00</published><updated>2018-11-19T00:00:00+00:00</updated><id>https://uncp.github.io/The-Art-of-Multiprocessor-Programming</id><content type="html" xml:base="https://uncp.github.io/The-Art-of-Multiprocessor-Programming/">&lt;h2 id=&quot;chapter-2&quot;&gt;Chapter 2&lt;/h2&gt;

&lt;h3 id=&quot;good-lock-algorithm&quot;&gt;Good &lt;code class=&quot;highlighter-rouge&quot;&gt;Lock&lt;/code&gt; Algorithm&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;strong&gt;Mutual exclusion&lt;/strong&gt;: critical section 不会发生重叠，即 $CS_{A}^{k}→CS_{B}^{j}$ or $CS_{B}^{j}→CS_{A}^{k}$&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Freedom From Deadlock&lt;/strong&gt;: 如果某个线程尝试加锁，那么某个线程会成功加锁。如果一个线程尝试加锁，但是始终不成功，那么一定是其他线程在完成无尽的 critical section&lt;/li&gt;
  &lt;li&gt;&lt;strong&gt;Freedom From Starvation&lt;/strong&gt;: 所有尝试加锁的线程最终都会成功（Lockout Freedom）&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;2-thread-solutions&quot;&gt;2-Thread Solutions&lt;/h3&gt;

&lt;h4 id=&quot;lockone-class&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;LockOne&lt;/code&gt; Class&lt;/h4&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LockOne&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// thread-local index, 0 or 1&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;        &lt;span class=&quot;c1&quot;&gt;// write true&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// read&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;满足1，&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;假设不满足1，那么 $lock_{A} → lock_{B}$ 成立，可以得出 $write_{A}(flag[A] = true) → read_{A}(flag[B] == false) → write_{B}(flag[B] = true) → read_{B}(flag[A] == false)$，即 $write_{A}(flag[A] = true) → read_{B}(flag[A] == false)$ ，矛盾，write true 操作后所有 read 操作都应该为 true，所以一定有 $unlock_{A}$ 操作发生在 $lock_{A}$ 和 $lock_{B}$ 之间。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;但是不满足2，&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;两个线程在 read 操作之前都进行了 write true 操作，所以 read 结果一直为真。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;locktwo-class&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;LockTwo&lt;/code&gt; Class&lt;/h4&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LockTwo&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;victim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;victim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;            &lt;span class=&quot;c1&quot;&gt;// let the other go first&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;victim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// wait&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;满足1，&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;$CS_{A}: write_{A}(victim = A) → read_{A}(victim == B)$&lt;/p&gt;

  &lt;p&gt;$CS_{B}: write_{B}(victim = B) → read_{B}(victim == A)$&lt;/p&gt;

  &lt;p&gt;假设不满足1，那么 $lock_{A} → lock_{B}$ 成立，可以得出 $write_{A}(victim = A) → write_{B}(victim = B) → read_{A}(victim == B)$，但是 $victim == B$ 与 $CS_{B}$ 矛盾。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;但是不满足2，&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;如果线程 B 在线程 A 之后执行，那么 B 将死锁。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;LockOne&lt;/code&gt; 与 &lt;code class=&quot;highlighter-rouge&quot;&gt;LockTwo&lt;/code&gt; 互为补充。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h4 id=&quot;the-peterson-lock&quot;&gt;The Peterson Lock&lt;/h4&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Peterson&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// thread-local index, 0 or 1&lt;/span&gt;
  &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;victim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;j&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// I’m interested&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;victim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;c1&quot;&gt;// you go first&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;j&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;victim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// wait&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// I’m not interested&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;满足1，&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;假设不满足1，那么 $lock_{A} → lock_{B}$ 成立，可以得出 $write_{A}(victim = A) → write_{B}(victim = B)$，即 $write_{B}(victim = B) → read_{B}(flag[A] == false)$，所以 $write_{A} (flag[A] = true) → write_{A} (victim = A) → write_{B}(victim = B) → read_{B}(flag[A] == false)$，即 $write_{A} (flag[A] = true) → read_{B}(flag[A] == false)$，矛盾。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;满足3，&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;如果不满足3，那么对于任意线程（假设是 A）来说一定是在 while 循环中，即  $flag[B] == true$ 或 $victim == A$，那么对于 B 来说当它进入 lock() 时，会将 victim 设为 B，并且不会改变，所以 A 一定会被唤醒，矛盾。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;满足3的同时一定满足2。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;the-filter-lock&quot;&gt;The Filter Lock&lt;/h3&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Filter&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;victim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Filter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;victim&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// use 1..n-1&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;me&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// attempt level i&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;me&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;victim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;me&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// spin while conflicts exist&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;∃&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;me&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;victim&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;me&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
      
  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;me&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;level&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;me&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;the-bakery-lock&quot;&gt;The Bakery Lock&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;二阶段保证公平性（fairness）
    &lt;ol&gt;
      &lt;li&gt;Doorway: 执行间隔是有限的&lt;/li&gt;
      &lt;li&gt;Waiting: 执行间隔可能是无限的&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
  &lt;p&gt;Frist Come Frist Served: 先结束 Doorway 阶段的线程先加锁&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kd&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Bakery&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;implements&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Lock&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;Bakery&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;boolean&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;];&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;++)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;lock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ThreadID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;...,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;∃&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;label&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{}&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;kd&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;unlock&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;flag&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ThreadID&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id=&quot;chapter-3&quot;&gt;Chapter 3&lt;/h2&gt;

&lt;p&gt;$Amdahl’s\ Law:\ S=\frac{1}{1-p+\frac{p}{n}}$&lt;/p&gt;

&lt;p&gt;$p$: 一个任务可以被并发执行的比例&lt;/p&gt;

&lt;p&gt;$n$: 线程数量&lt;/p&gt;

&lt;p&gt;### quiescent(relaxed) consistency&lt;/p&gt;

&lt;h3 id=&quot;sequential-consistency&quot;&gt;Sequential Consistency&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;定义：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The result of any execution is the same as if the operations of all the processors were executed in some sequential order, and the operations of each individual processor appear in this sequence in the order specified by its program.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;要求：&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Each processor issues memory requests in the order specified by it’s program，&lt;em&gt;first-appear, first-serve&lt;/em&gt;&lt;/li&gt;
  &lt;li&gt;Memory requests from all processors issued to an individual memory module are serviced from a single FIFO queue. Issuing a memory request consists of entering the request on this queue, &lt;em&gt;first-come, first-serve&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;注意：&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;不保证 real time ordering，以下序列是合法的：&lt;/p&gt;

    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Enq(x) A
Ok()   A
Enq(y) B
Deq()  A
Ok()   B
Ok(y)  A
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;没有局部性（locality）&lt;/p&gt;

    &lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;p Enq(x) A
p Ok()   A
q Enq(y) B   (1)
q Ok()   B
q Enq(x) A
q Ok()   A
p Enq(y) B   (2)
p Ok()   B
p Deq()  A
p Ok(y)  A   (3)
q Deq()  B
q Ok(x)  B   (4)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;

    &lt;p&gt;对于 p 和 q 来说满足顺序一致性，但是作为一个整体不满足，因为对于线程 A 来说，如果 (3) 成立，那么意味着 B 先执行了 (2)，同时意味着 B 先执行了 (1)，在这种情况下 (4) 无法成立。&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;非阻塞&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;linearizability&quot;&gt;Linearizability&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;定义：&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;$&amp;lt;&lt;em&gt;{H}: e&lt;/em&gt;{0} &amp;lt;&lt;em&gt;{H} e&lt;/em&gt;{1}\;if\;res(e_{0})\;precedes\;inv(e_{1})\;in\;H.$&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
  &lt;li&gt;complete(H’) 等价于某个合法化的顺序华历史 S&lt;/li&gt;
  &lt;li&gt;$&amp;lt;&lt;em&gt;{H}\;\subseteq\;&amp;lt;&lt;/em&gt;{S}$&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;局部性，历史 H 中每个对象 x 是线性化的，才能保证 H 是线性化的。&lt;/li&gt;
  &lt;li&gt;非阻塞&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;serializable&quot;&gt;Serializable&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;定义：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;每个事务都没有交叉地顺序执行&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3 id=&quot;strict-serializable&quot;&gt;Strict Serializable&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;定义：&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;每个事务在顺序化历史中的顺序和发生顺序相匹配，通常由二阶段锁保证，而不是 MVCC，也不是网络分区中提供 availability 的方法。&lt;/p&gt;

&lt;p&gt;线性化可被视为对单个对象进行单个操作的特殊 strict serializability。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;特点：&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;没有局部性，如 sequential consistency 中的 2（将 A 和 B 视为事务）&lt;/li&gt;
  &lt;li&gt;阻塞&lt;/li&gt;
&lt;/ol&gt;</content><author><name>UncP</name><email>uncp.xu@gmail.com</email></author><summary type="html">Chapter 2</summary></entry><entry><title type="html">Jemalloc</title><link href="https://uncp.github.io/JeMalloc/" rel="alternate" type="text/html" title="Jemalloc" /><published>2018-11-16T00:00:00+00:00</published><updated>2018-11-16T00:00:00+00:00</updated><id>https://uncp.github.io/JeMalloc</id><content type="html" xml:base="https://uncp.github.io/JeMalloc/">&lt;h2 id=&quot;jemalloc&quot;&gt;JeMalloc&lt;/h2&gt;
&lt;p&gt;JeMalloc 是一款内存分配器，与其它内存分配器相比，它最大的优势在于多线程情况下的高性能以及内存碎片的减少。&lt;/p&gt;

&lt;p&gt;这篇文章介绍 &lt;strong&gt;JeMalloc-5.1.0&lt;/strong&gt; 版本（release 日期：2018年5月9日）的实现细节。&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;p&gt;对于对老版本比较熟悉的人来说，有几点需要说明：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;chunk 这一概念被替换成了 extent&lt;/li&gt;
  &lt;li&gt;dirty page 的 decay（或者说 gc） 变成了两阶段，dirty -&amp;gt; muzzy -&amp;gt; retained&lt;/li&gt;
  &lt;li&gt;huge class 这一概念不再存在&lt;/li&gt;
  &lt;li&gt;红黑树不再使用，取而代之的是 pairing heap&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h2 id=&quot;基础知识&quot;&gt;基础知识&lt;/h2&gt;

&lt;p&gt;以下内容介绍 JeMalloc 中比较重要的概念以及数据结构。&lt;/p&gt;

&lt;h4 id=&quot;size_class&quot;&gt;size_class&lt;/h4&gt;

&lt;p&gt;每个 &lt;code class=&quot;highlighter-rouge&quot;&gt;size_class&lt;/code&gt; 代表 jemalloc 分配的内存大小，共有 NSIZES（232）个小类（如果用户申请的大小位于两个小类之间，会取较大的，比如申请14字节，位于8和16字节之间，按16字节分配），分为2大类：&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;small_class&lt;/code&gt;（&lt;em&gt;小内存&lt;/em&gt;） : 对于64位机器来说，通常区间是 [8, 14kb]，常见的有 8, 16, 32, 48, 64, …, 2kb, 4kb, 8kb，注意为了减少内存碎片并不都是2的次幂，比如如果没有48字节，那当申请33字节时，分配64字节显然会造成约50%的内存碎片&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;large_class&lt;/code&gt;（&lt;em&gt;大内存&lt;/em&gt;）: 对于64位机器来说，通常区间是 [16kb, 7EiB]，从 4 * page_size 开始，常见的比如 16kb, 32kb, …, 1mb, 2mb, 4mb，最大是 $2^{62}+3^{60}$&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;size_index&lt;/code&gt; :  size 位于 &lt;code class=&quot;highlighter-rouge&quot;&gt;size_class&lt;/code&gt; 中的索引号，区间为 [0，231]，比如8字节则为0，14字节（按16计算）为1，4kb字节为28，当 size 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;small_class&lt;/code&gt; 时，&lt;code class=&quot;highlighter-rouge&quot;&gt;size_index&lt;/code&gt; 也称作 &lt;code class=&quot;highlighter-rouge&quot;&gt;binind&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;base&quot;&gt;base&lt;/h4&gt;

&lt;p&gt;用于分配 jemalloc 元数据内存的结构，通常一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;base&lt;/code&gt; 大小为 2mb， 所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;base&lt;/code&gt; 组成一个链表。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;base.extents[NSIZES]&lt;/code&gt; : 存放每个 &lt;code class=&quot;highlighter-rouge&quot;&gt;size_class&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 元数据&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;bin&quot;&gt;bin&lt;/h4&gt;

&lt;p&gt;管理正在使用中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;slab&lt;/code&gt;（即用于小内存分配的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;） 的集合，每个 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin&lt;/code&gt; 对应一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;size_class&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;bin.slabcur&lt;/code&gt; : 当前使用中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;slab&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;bin.slabs_nonfull&lt;/code&gt; : 有空闲内存块的 &lt;code class=&quot;highlighter-rouge&quot;&gt;slab&lt;/code&gt;&lt;/p&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;extent&quot;&gt;extent&lt;/h4&gt;

&lt;p&gt;管理 jemalloc 内存块（即用于用户分配的内存）的结构，每一个内存块大小可以是 &lt;code class=&quot;highlighter-rouge&quot;&gt;N * page_size(4kb)&lt;/code&gt;（N &amp;gt;= 1）。每个 extent 有一个序列号（serial number）。&lt;/p&gt;

&lt;p&gt;一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 可以用来分配一次 &lt;code class=&quot;highlighter-rouge&quot;&gt;large_class&lt;/code&gt; 的内存申请，但可以用来分配多次 &lt;code class=&quot;highlighter-rouge&quot;&gt;small_class&lt;/code&gt; 的内存申请。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;extent.e_bits&lt;/code&gt; : 8字节长，记录多种信息&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;extent.e_addr&lt;/code&gt; : 管理的内存块的起始地址&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;extent.e_slab_data&lt;/code&gt; : 位图，当此 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 用于分配 &lt;code class=&quot;highlighter-rouge&quot;&gt;small_class&lt;/code&gt; 内存时，用来记录这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的分配情况，此时每个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的内的小内存称为 &lt;code class=&quot;highlighter-rouge&quot;&gt;region&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;slab&quot;&gt;slab&lt;/h4&gt;

&lt;p&gt;当 extent 用于分配 &lt;code class=&quot;highlighter-rouge&quot;&gt;small_class&lt;/code&gt; 内存时，称其为 &lt;code class=&quot;highlighter-rouge&quot;&gt;slab&lt;/code&gt;。一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 可以被用来处理多个同一 &lt;code class=&quot;highlighter-rouge&quot;&gt;size_class&lt;/code&gt; 的内存申请。&lt;/p&gt;

&lt;h4 id=&quot;extents&quot;&gt;extents&lt;/h4&gt;

&lt;p&gt;管理 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的集合。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;extents.heaps[NPSIZES+1]&lt;/code&gt; : 各种 &lt;code class=&quot;highlighter-rouge&quot;&gt;page(4kb)&lt;/code&gt; 倍数大小的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;extents.lru&lt;/code&gt; : 存放所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的双向链表&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;extents.delay_coalesce&lt;/code&gt; : 是否延迟 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的合并&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;arena&quot;&gt;arena&lt;/h4&gt;

&lt;p&gt;用于分配&amp;amp;回收 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的结构，每个用户线程会被绑定到一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 上，默认每个逻辑 CPU 会有 4 个 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 来减少锁的竞争，各个 arena 所管理的内存相互独立。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;arena.extents_dirty&lt;/code&gt; : 刚被释放后空闲 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 位于的地方&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;arena.extents_muzzy&lt;/code&gt; : &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_dirty&lt;/code&gt; 进行 lazy purge 后位于的地方，&lt;code class=&quot;highlighter-rouge&quot;&gt;dirty -&amp;gt; muzzy&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;arena.extents_retained&lt;/code&gt; : &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_muzzy&lt;/code&gt; 进行 decommit 或 force purge 后 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 位于的地方，&lt;code class=&quot;highlighter-rouge&quot;&gt;muzzy -&amp;gt; retained&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;arena.large&lt;/code&gt; : 存放 &lt;code class=&quot;highlighter-rouge&quot;&gt;large extent&lt;/code&gt; 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;arena.extent_avail&lt;/code&gt; : heap，存放可用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 元数据&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;arena.bins[NBINS]&lt;/code&gt; : 所以用于分配小内存的 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;arena.base&lt;/code&gt; : 用于分配元数据的 &lt;code class=&quot;highlighter-rouge&quot;&gt;base&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;内存状态&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;备注&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;clean&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;分配给用户或 &lt;code class=&quot;highlighter-rouge&quot;&gt;tcache&lt;/code&gt;&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;dirty&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;用户调用 free 或 &lt;code class=&quot;highlighter-rouge&quot;&gt;tcache&lt;/code&gt; 进行了 gc&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;muzzy&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;extents_dirty&lt;/code&gt; 对 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 进行 lazy purge&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;retained&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;extents_muzzy&lt;/code&gt; 对 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 进行了 decommit 或 force purge&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;blockquote&gt;
  &lt;p&gt;purge 及 decommit 在内存 gc 模块介绍&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4 id=&quot;rtree&quot;&gt;rtree&lt;/h4&gt;

&lt;p&gt;全局唯一的存放每个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 信息的 Radix Tree，以 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent-&amp;gt;e_addr&lt;/code&gt; 即 &lt;code class=&quot;highlighter-rouge&quot;&gt;uintptr_t&lt;/code&gt; 为 key，以我的机器为例，&lt;code class=&quot;highlighter-rouge&quot;&gt;uintptr_t&lt;/code&gt;  为64位（8字节）， &lt;code class=&quot;highlighter-rouge&quot;&gt;rtree&lt;/code&gt; 的高度为3，由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent-&amp;gt;e_addr&lt;/code&gt; 是 &lt;code class=&quot;highlighter-rouge&quot;&gt;page(1 &amp;lt;&amp;lt; 12)&lt;/code&gt; 对齐的，也就是说需要 64 - 12 = 52 位即可确定在树中的位置，每一层分别通过第0-16位，17-33位，34-51位来进行访问。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/radix_tree.png&quot; alt=&quot;rtree&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;cache_bin&quot;&gt;cache_bin&lt;/h4&gt;

&lt;p&gt;每个线程独有的用于分配小内存的缓存&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;low_water&lt;/code&gt; : 上一次 gc 后剩余的缓存数量&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin.ncached&lt;/code&gt; : 当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin&lt;/code&gt; 存放的缓存数量&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin.avail&lt;/code&gt; : 可直接用于分配的内存，从左往右依次分配（注意这里的寻址方式）&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/cache_bin.png&quot; alt=&quot;cache_bin&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;tcache&quot;&gt;tcache&lt;/h4&gt;

&lt;p&gt;每个线程独有的缓存（Thread Cache），大多数内存申请都可以在 &lt;code class=&quot;highlighter-rouge&quot;&gt;tcache&lt;/code&gt; 中直接得到，从而避免加锁&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tcache.bins_small[NBINS]&lt;/code&gt; : 小内存的 &lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h4 id=&quot;tsd&quot;&gt;tsd&lt;/h4&gt;

&lt;p&gt;Thread Specific Data，每个线程独有，用于存放与这个线程相关的结构&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tsd.rtree_ctx&lt;/code&gt; : 当前线程的 rtree context，用于快速访问 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 信息&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tsd.arena&lt;/code&gt; : 当前线程绑定的 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tsd.tcache&lt;/code&gt; : 当前线程的 &lt;code class=&quot;highlighter-rouge&quot;&gt;tcache&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;内存分配malloc&quot;&gt;内存分配（malloc）&lt;/h2&gt;

&lt;h4 id=&quot;小内存small_class分配&quot;&gt;小内存(small_class)分配&lt;/h4&gt;

&lt;p&gt;首先从 &lt;code class=&quot;highlighter-rouge&quot;&gt;tsd-&amp;gt;tcache-&amp;gt;bins_small[binind]&lt;/code&gt; 中获取对应 &lt;code class=&quot;highlighter-rouge&quot;&gt;size_class&lt;/code&gt; 的内存，有的话将内存直接返回给用户，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;bins_small[binind]&lt;/code&gt; 中没有的话，需要通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;slab(extent)&lt;/code&gt; 对 &lt;code class=&quot;highlighter-rouge&quot;&gt;tsd-&amp;gt;tcache-&amp;gt;bins_small[binind]&lt;/code&gt; 进行填充，一次填充多个以备后续分配，填充方式如下（当前步骤无法成功则进行下一步）：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin-&amp;gt;slabcur&lt;/code&gt; 分配&lt;/li&gt;
  &lt;li&gt;从 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin-&amp;gt;slabs_nonfull&lt;/code&gt; 中获取可使用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;从 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena-&amp;gt;extents_dirty&lt;/code&gt; 中回收 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，回收方式为 &lt;strong&gt;&lt;em&gt;best-fit&lt;/em&gt;&lt;/strong&gt;，即满足大小要求的&lt;strong&gt;最小&lt;/strong&gt; &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，在 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena-&amp;gt;extents_dirty-&amp;gt;bitmap&lt;/code&gt; 中找到满足大小要求并且第一个非空 heap 的索引 &lt;code class=&quot;highlighter-rouge&quot;&gt;i&lt;/code&gt;，然后从 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents-&amp;gt;heaps[i]&lt;/code&gt; 中获取第一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;。由于 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 可能较大，为了防止产生内存碎片，需要对 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 进行分裂（伙伴算法），然后将分裂后不使用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 放回 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_dirty&lt;/code&gt; 中&lt;/li&gt;
  &lt;li&gt;从 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena-&amp;gt;extents_muzzy&lt;/code&gt; 中回收 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，回收方式为 &lt;strong&gt;&lt;em&gt;first-fit&lt;/em&gt;&lt;/strong&gt;，即满足大小要求且&lt;strong&gt;序列号最小地址最低（最旧）&lt;/strong&gt;的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，遍历每个满足大小要求并且非空的 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena-&amp;gt;extents_dirty-&amp;gt;bitmap&lt;/code&gt;，获取其对应 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents-&amp;gt;heaps&lt;/code&gt; 中第一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，然后进行比较，找到最旧的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，之后仍然需要分裂&lt;/li&gt;
  &lt;li&gt;从 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena-&amp;gt;extents_retained&lt;/code&gt; 中回收 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，回收方式与 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_muzzy&lt;/code&gt; 相同&lt;/li&gt;
  &lt;li&gt;尝试通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;mmap&lt;/code&gt; 向内核获取所需的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 内存，并且在 &lt;code class=&quot;highlighter-rouge&quot;&gt;rtree&lt;/code&gt; 中注册新 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的信息&lt;/li&gt;
  &lt;li&gt;再次尝试从 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin-&amp;gt;slabs_nonfull&lt;/code&gt; 中获取可使用的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;简单来说，这个流程是这样的，&lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin -&amp;gt; slab -&amp;gt; slabs_nonfull -&amp;gt; extents_dirty -&amp;gt; extents_muzzy -&amp;gt; extents_retained -&amp;gt; kernal&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/small_alloc.png&quot; alt=&quot;small_class_alloc&quot; /&gt;&lt;/p&gt;

&lt;h4 id=&quot;大内存large_class分配&quot;&gt;大内存(large_class)分配&lt;/h4&gt;

&lt;p&gt;大内存不存放在 &lt;code class=&quot;highlighter-rouge&quot;&gt;tsd-&amp;gt;tcache&lt;/code&gt; 中，因为这样可能会浪费内存，所以每次申请都需要重新分配一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，申请的流程和小内存申请 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 流程中的3, 4, 5, 6是一样的。&lt;/p&gt;

&lt;h2 id=&quot;内存释放free&quot;&gt;内存释放（free）&lt;/h2&gt;

&lt;h4 id=&quot;小内存释放&quot;&gt;小内存释放&lt;/h4&gt;

&lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;rtree&lt;/code&gt; 中找到需要被释放内存所属的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 信息，将要被释放的内存还给 &lt;code class=&quot;highlighter-rouge&quot;&gt;tsd-&amp;gt;tcache-&amp;gt;bins_small[binind]&lt;/code&gt;，如果 &lt;code class=&quot;highlighter-rouge&quot;&gt;tsd-&amp;gt;tcache-&amp;gt;bins_small[binind]&lt;/code&gt; 已满，需要对其进行 flush，流程如下：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;将这块内存返还给所属 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，如果这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 中空闲的内存块变成了最大（即没有一份内存被分配），跳到2；如果这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 中的空闲块变成了1并且这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 不是 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena-&amp;gt;bins[binind]-&amp;gt;slabcur&lt;/code&gt;，跳到3&lt;/li&gt;
  &lt;li&gt;将这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 释放，即插入 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena-&amp;gt;extents_dirty&lt;/code&gt; 中&lt;/li&gt;
  &lt;li&gt;将  &lt;code class=&quot;highlighter-rouge&quot;&gt;arena-&amp;gt;bins[binind]-&amp;gt;slabcur&lt;/code&gt; 切换为这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，前提是这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; “更旧”（序列号更小地址更低），并且将替换后的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 移入 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena-&amp;gt;bins[binind]-&amp;gt;slabs_nonfull&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;大内存释放&quot;&gt;大内存释放&lt;/h4&gt;

&lt;p&gt;因为大内存不存放在 &lt;code class=&quot;highlighter-rouge&quot;&gt;tsd-&amp;gt;tcache&lt;/code&gt; 中，所以大内存释放只进行小内存释放的步骤2，即将 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 插入 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena-&amp;gt;extents_dirty&lt;/code&gt; 中。&lt;/p&gt;

&lt;h2 id=&quot;内存再分配realloc&quot;&gt;内存再分配（realloc）&lt;/h2&gt;

&lt;h4 id=&quot;小内存再分配&quot;&gt;小内存再分配&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;尝试进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;no move&lt;/code&gt; 分配，如果两次申请位于同一 &lt;code class=&quot;highlighter-rouge&quot;&gt;size class&lt;/code&gt; 的话就可以不做任何事情，直接返回。比如第一次申请了12字节，但实际上 jemalloc 会实际分配16字节，然后第二次申请将12扩大到15字节或者缩小到9字节，那这时候16字节就已经满足需求了，所以不做任何事情，如果无法满足，跳到2&lt;/li&gt;
  &lt;li&gt;重新分配，申请新内存大小（参考&lt;strong&gt;内存分配&lt;/strong&gt;），然后将旧内存内容拷贝到新地址，之后释放旧内存（参考&lt;strong&gt;内存释放&lt;/strong&gt;），最后返回新内存&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;大内存再分配&quot;&gt;大内存再分配&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;尝试进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;no move&lt;/code&gt; 分配，如果两次申请位于同一 &lt;code class=&quot;highlighter-rouge&quot;&gt;size class&lt;/code&gt; 的话就可以不做任何事情，直接返回。&lt;/li&gt;
  &lt;li&gt;尝试进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;no move resize&lt;/code&gt; 分配，如果第二次申请的大小大于第一次，则尝试对当前地址所属 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的下一地址查看是否可以分配，比如当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 地址是 0x1000，大小是 0x1000，那么我们查看地址 0x2000 开始的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 是否存在（通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;rtree&lt;/code&gt;）并且是否满足要求，如果满足要求那两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 可以进行合并，成为一个新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 而不需要重新分配；如果第二次申请的大小小于第一次，那么尝试对当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 进行 split，移除不需要的后半部分，以减少内存碎片；如果无法进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;no move resize&lt;/code&gt; 分配，跳到3&lt;/li&gt;
  &lt;li&gt;重新分配，申请新内存大小（参考&lt;strong&gt;内存分配&lt;/strong&gt;），然后将旧内存内容拷贝到新地址，之后释放旧内存（参考&lt;strong&gt;内存释放&lt;/strong&gt;），最后返回新内存&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;内存-gc&quot;&gt;内存 GC&lt;/h2&gt;

&lt;p&gt;分为2种， &lt;code class=&quot;highlighter-rouge&quot;&gt;tcache&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;  GC。其实更准确来说是 decay，为了方便还是用 gc 吧。&lt;/p&gt;

&lt;h3 id=&quot;tcache-gc&quot;&gt;tcache GC&lt;/h3&gt;

&lt;p&gt;针对 &lt;code class=&quot;highlighter-rouge&quot;&gt;small_class&lt;/code&gt;，防止某个线程预先分配了内存但是却没有实际分配给用户，会定期将缓存 flush 到 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GC 策略&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;每次对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;tcache&lt;/code&gt; 进行 malloc 或者 free 操作都会执行一次计数，默认情况下达到228次就会触发 gc，每次 gc 一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;如何 GC&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin.low_water &amp;gt; 0&lt;/code&gt; : gc 掉 &lt;code class=&quot;highlighter-rouge&quot;&gt;low_water&lt;/code&gt; 的 3/4，同时，将 &lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin&lt;/code&gt; 能缓存的最大数量缩小一倍&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin.low_water &amp;lt; 0&lt;/code&gt; : 将 &lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin&lt;/code&gt; 能缓存的最大数量增大一倍&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;总的来说保证当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin&lt;/code&gt; 分配越频繁，则会缓存更多的内存，否则则会减少。&lt;/p&gt;

&lt;h3 id=&quot;extent-gc&quot;&gt;extent GC&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;调用 free 时，内存并没有归还给内核&lt;/strong&gt;。 jemalloc 内部会不定期地将没有用于分配的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 逐步 GC，流程和内存申请是反向的， &lt;code class=&quot;highlighter-rouge&quot;&gt;free -&amp;gt; extents_dirty -&amp;gt; extents_muzzy -&amp;gt; extents_retained -&amp;gt; kernal&lt;/code&gt;。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GC 策略&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;默认10s为 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_dirty&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_muzzy&lt;/code&gt; 的一个 gc 周期，每次对于 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 进行 malloc 或者 free 操作都会执行一次计数，达到1000次会检测有没有达到 gc 的 deadline，如果是的话进行 gc。&lt;/p&gt;

&lt;p&gt;注意并不是每隔10s一次性 gc，实际上 jemalloc 会将10s划分成200份，即每隔0.05s进行一次 gc，这样一来如果 t 时刻有 N 个 &lt;code class=&quot;highlighter-rouge&quot;&gt;page&lt;/code&gt; 需要 gc，那么 jemalloc 尽力保证在 t+10 时刻这 N 个 &lt;code class=&quot;highlighter-rouge&quot;&gt;page&lt;/code&gt; 会被 gc 完成。&lt;/p&gt;

&lt;p&gt;严格来说，对于两次 gc 时刻 $t_{1}$ 和 $t_{2}$，在 $t_{2}-t_{1}$ 时间段内产生的所有 &lt;code class=&quot;highlighter-rouge&quot;&gt;page&lt;/code&gt;（dirty page 或 muzzy page） 会在 ($t_{2}$, $ t_{2}+10$] 被 gc 完成。&lt;/p&gt;

&lt;p&gt;对于 N 个需要 gc 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;page&lt;/code&gt; 来说，并不是简单地每0.05s处理 N/200 个 &lt;code class=&quot;highlighter-rouge&quot;&gt;page&lt;/code&gt;，jemalloc 引入了 &lt;strong&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;Smoothstep&lt;/code&gt;&lt;/strong&gt;（主要用于计算机图形学）来获得更加平滑的 gc 机制，这是 jemalloc 非常有意思的一个点。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../../assets/images/smoothstep.svg&quot; alt=&quot;smoothstep&quot; /&gt;&lt;/p&gt;

&lt;p&gt;jemalloc 内部维护了一个长度为200的数组，用来计算在10s的 gc 周期内每个时间点应该对多少 &lt;code class=&quot;highlighter-rouge&quot;&gt;page&lt;/code&gt; 进行 gc。这样保证两次 gc 的时间段内产生的需要 gc 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;page&lt;/code&gt; 都会以图中绿色线条（默认使用 smootherstep）的变化曲线在10s的周期内从 N 减为 0（从右往左）。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;如何 GC&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;先进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_dirty&lt;/code&gt; 的 gc，后进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_muzzy&lt;/code&gt; 。&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;将 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_dirty&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 移入 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_muzzy&lt;/code&gt;：
    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_dirty&lt;/code&gt; 中的 LRU 链表中，获得要进行 gc 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，尝试对 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 进行前后合并（前提是两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 位于同一 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 并且位于同一 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents&lt;/code&gt; 中），获得新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，然后将其移除&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;对当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 管理的地址进行 lazy purge，即通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;madvise&lt;/code&gt; 使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MADV_FREE&lt;/code&gt; 参数告诉内核当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 管理的内存可能不会再被访问&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_muzzy&lt;/code&gt; 中尝试对当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 进行前后合并，获得新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，最后将其插入 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_muzzy&lt;/code&gt;&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;将 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_muzzy&lt;/code&gt; 中的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 移入 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_retained&lt;/code&gt; :
    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_muzzy&lt;/code&gt; 中的 LRU 链表中，获得要进行 gc 的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，尝试对 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 进行前后合并，获得新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，然后将其移除&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;对当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 管理的地址进行 decommit，即调用 &lt;code class=&quot;highlighter-rouge&quot;&gt;mmap&lt;/code&gt; 带上 &lt;code class=&quot;highlighter-rouge&quot;&gt;PROT_NONE&lt;/code&gt; 告诉内核当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 管理的地址可能不会再被访问，如果 decommit 失败，会进行 force purge，即通过 &lt;code class=&quot;highlighter-rouge&quot;&gt;madvise&lt;/code&gt; 使用 &lt;code class=&quot;highlighter-rouge&quot;&gt;MADV_DONTNEED&lt;/code&gt; 参数告诉内核当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 管理的内存可能不会再被访问&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;在 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_retained&lt;/code&gt; 中尝试对当前 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 进行前后合并，获得新的 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt;，最后将其插入 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_retained&lt;/code&gt;&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;jemalloc 默认不会将内存归还给内核，只有进程结束时，所有内存才会 &lt;code class=&quot;highlighter-rouge&quot;&gt;munmap&lt;/code&gt;，从而归还给内核。不过可以手动进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 的销毁，从而将 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents_retained&lt;/code&gt; 中的内存进行 &lt;code class=&quot;highlighter-rouge&quot;&gt;munmap&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;内存碎片&quot;&gt;内存碎片&lt;/h2&gt;

&lt;p&gt;JeMalloc 保证内部碎片在20%左右。对于绝大多数 &lt;code class=&quot;highlighter-rouge&quot;&gt;size_class&lt;/code&gt; 来说，都属于 $2^{x}$ 的 group $y$，比如 160，192，224，256都属于 $2^{8-1}$ 的 group 7。对于一个 group 来说，会有4个 &lt;code class=&quot;highlighter-rouge&quot;&gt;size_class&lt;/code&gt;，每个 size 的大小计算是这样的，$(1 « y) + (i « (y-2))$，其中 i 为在这个 group 中的索引（1，2，3，4），比如 160 为 $(1 « 7) + (1 « 5)$，即 $5 * 2^{7-2}$。&lt;/p&gt;

&lt;p&gt;对于两组 group 来说：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Group&lt;/th&gt;
      &lt;th style=&quot;text-align: center&quot;&gt;Size&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;y&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;$5 * 2^{y-2}$, $6 * 2^{y-2}$, $7 * 2^{y-2}$, $8 * 2^{y-2}$&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;y+1&lt;/td&gt;
      &lt;td style=&quot;text-align: center&quot;&gt;$5 * 2^{y-1}$, $6 * 2^{y-1}$, $7 * 2^{y-1}$, $8 * 2^{y-1}$&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;取相差最大的第一组的最后一个和第二组的第一个，内存碎片约为 $\frac{5 * 2^{y-1} - 8 * 2^{y-2} + 1}{5 * 2^{y-1}}$ 约等于 20%。&lt;/p&gt;

&lt;h2 id=&quot;jemalloc-实现上的优缺点&quot;&gt;JeMalloc 实现上的优缺点&lt;/h2&gt;

&lt;h4 id=&quot;优点&quot;&gt;优点&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;采用多个 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 来避免线程同步&lt;/li&gt;
  &lt;li&gt;细粒度的锁，比如每一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;bin&lt;/code&gt; 以及每一个 &lt;code class=&quot;highlighter-rouge&quot;&gt;extents&lt;/code&gt; 都有自己的锁&lt;/li&gt;
  &lt;li&gt;Memory Order 的使用，比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;rtree&lt;/code&gt; 的读写访问有不同的原子语义（relaxed, acquire, release）&lt;/li&gt;
  &lt;li&gt;结构体以及内存分配时保证对齐，以获得更好的 cache locality&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;cache_bin&lt;/code&gt; 分配内存时会通过栈变量来判断是否成功以避免 cache miss&lt;/li&gt;
  &lt;li&gt;dirty &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的 delay coalesce 来获得更好的 cache locality；&lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的 lazy purge 来保证更平滑的 gc 机制&lt;/li&gt;
  &lt;li&gt;紧凑的结构体内存布局来减少占用空间，比如 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent.e_bits&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;rtree&lt;/code&gt; 引入 &lt;code class=&quot;highlighter-rouge&quot;&gt;rtree_ctx&lt;/code&gt; 的两级 &lt;code class=&quot;highlighter-rouge&quot;&gt;cache&lt;/code&gt; 机制，提升 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 信息获取速度的同时减少 cache miss&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;tcache&lt;/code&gt; gc 时对缓存容量的动态调整&lt;/li&gt;
&lt;/ol&gt;

&lt;h4 id=&quot;缺点&quot;&gt;缺点&lt;/h4&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 之间的内存不可见
    &lt;ul&gt;
      &lt;li&gt;某个线程在这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 使用了很多内存，之后这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 并没有其他线程使用，导致这个 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 的内存无法被 gc，占用过多&lt;/li&gt;
      &lt;li&gt;两个位于不同 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 的线程频繁进行内存申请，导致两个 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 的内存出现大量交叉，但是连续的内存由于在不同 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt; 而无法进行合并&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;目前只想到了一个&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;总结&quot;&gt;总结&lt;/h2&gt;

&lt;p&gt;文章开头说 JeMalloc 的优点在于多线程下的性能以及内存碎片的减少，对于保证多线程性能，有不同 &lt;code class=&quot;highlighter-rouge&quot;&gt;arena&lt;/code&gt;、降低锁的粒度、使用原子语义等等；对于内存碎片的减少，有经过设计的多种 &lt;code class=&quot;highlighter-rouge&quot;&gt;size_class&lt;/code&gt;、伙伴算法、gc 等等。&lt;/p&gt;

&lt;p&gt;阅读 JeMalloc 源码的意义不光在于能够精确描述每次 malloc 和 free 会发生什么，还在于学习内存分配器如何管理内存。malloc 和 free 是静态的释放和分配，而 &lt;code class=&quot;highlighter-rouge&quot;&gt;tcache&lt;/code&gt; 和 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的 gc 则是动态的管理，熟悉后者同样非常重要。&lt;/p&gt;

&lt;p&gt;除此以外还能够帮助自己在编程时根据相应的内存使用特征去选择合适的内存分配方法，甚至使用自己实现的特定内存分配器。&lt;/p&gt;

&lt;p&gt;最后个人觉得 jemalloc 最有意思的地方就在于 &lt;code class=&quot;highlighter-rouge&quot;&gt;extent&lt;/code&gt; 的曲线 gc 了。&lt;/p&gt;

&lt;h2 id=&quot;参考&quot;&gt;参考&lt;/h2&gt;

&lt;p&gt;&lt;a href=&quot;http://jemalloc.net/jemalloc.3.html&quot;&gt;JeMalloc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://youjiali1995.github.io/allocator/jemalloc/&quot;&gt;JeMalloc-4.0.3&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://youjiali1995.github.io/allocator/jemalloc-purge/&quot;&gt;JeMalloc-Purge&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/29216091&quot;&gt;图解 TCMalloc&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/29415507&quot;&gt;TCMalloc分析 - 如何减少内存碎片&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://goog-perftools.sourceforge.net/doc/tcmalloc.html&quot;&gt;TCMalloc&lt;/a&gt;&lt;/p&gt;</content><author><name>UncP</name><email>uncp.xu@gmail.com</email></author><summary type="html">JeMalloc JeMalloc 是一款内存分配器，与其它内存分配器相比，它最大的优势在于多线程情况下的高性能以及内存碎片的减少。</summary></entry></feed>