并发写Btree原理剖析 一文中提到,节点内存回收有可能导致内存突增以及影响写性能。本文将阐述最近对内存回收的改进,多线程可并行回收内存。

回收策略

采用基于版本的机制,Btree全局维护一个版本号V,一个全局数组A,每个线程一个槽位,每个线程读写操作之前获取全局版本号V,记作V0,并且记录到全局数组A中自己所属的槽位,表明对应线程不会再访问版本号V0之前的节点。写操作结束后,获取全局版本号V记录到这次写操作的TRecycleNode中,然后将V原子加1,这样来推进Btree的版本号递增,最后将这个TRecycleNode挂在线程局部的TRecycleNode的链表尾部。回收内存时,只需要遍历全局数组A,找到最小版本号记作minV,每个线程只回收线程局部的版本号小于minV的所有的TRecycleNode及其内部的TBtreeNode。可以看出,在目前这种回收策略中,不存在全局的TRecycleNode链表,线程不用抢锁,各自回收自己局部内存即可。

这里有一个问题:如果某个线程由于某些原因,很少对Btree进行读写,那么全局数组A的最小值永远不变,导致其他线程也不能回收内存。针对这种情况,可以在每次读写操作结束后,将线程在全局数组中对应的槽位置为无穷大。

这就带来了另外一个问题:一个线程刚拿到全局版本号V,记作V0,还没有来得及放入全局数组A对应槽位,随后一些写操作完成全局版本号V被递增,然后某个线程开始回收,扫描了全局数组并且已经计算出了最小版本V1,这时V1 > V0, 代表V0可以被回收,这时,第一个线程读V0就会出现问题。解决这个问题,可以使用类似于hazard pointer的机制,读写开始获取版本号和获取回收版本号的过程分别如下:

读写开始拿可用版本号的过程:

1. 拿全局版本号V放入局部变量V0:    V0 <- V

2. 将V0写入全局数组中对应的槽位中:  A[slot] <- V0

3. 如果 V0 = V 则可以开始安全的读写,版本号为V0,否则重试,跳第一步。

写线程回收时拿到可回收版本号的过程:

1.  拿全局版本号V放入局部变量V1:  V1 <- V

2.  V2 <- min(A)

3.  V3 <- min(V1,V2) V3之前的版本即为可以回收的版本

证明:

1.  如果V1 <= V0:则V3 = min(V1,V2) <= V1 <= V0,读写拿到的版本号V0 大于等于回收版本号

2.  如果V1 > V0:如果读写过程的第三步成立V0 = V,那么,回收过程的第二步V2 <- min(A)计算最小值时已经看到了读写过程写入全局数组A中的版本V0,即V3 = min(V1,V2) <= V0

可以看出,这种方式对于正常的Btree读写流程来说,可能第3步需要进行重试: 在第一步和第三步之间的最多的几十ns期间全局版本号V被其它写线程递增。当然,这种情况还算是比较少。为了进一步的避免这个问题,可以采用如下方法:

维护一个全局可回收版本号recycleV,它比V小一些,比如100个版本,每个线程回收时只会回收小于recycleV的TRecycleNode,读写过程和回收过程算法如下:

读写开始拿可用版本号的过程:

1. 拿全局版本号V放入局部变量V0:    V0 <- V

2. 将V0写入全局数组中对应的槽位中:  A[slot] <- V0

3. 如果 V0 > recycleV 则可以开始安全的读写,版本号为V0,否则重试,跳第一步。

写线程回收时拿到可回收版本号的过程:

1. recycleV <- V – 100  多线程需要保证recycleV递增

2. V2 <- min(A)

3. V3 <- min(recycleV,V2)   小于V3的版本即为可以回收的版本

这种优化相当于让回收线程滞后100个版本回收。

还有其它一些小优化,比如全局数组A每个元素cache line对齐,避免false sharing,线程局部可用节点太多,归还一些到全局内存池等。

分析

从读写操作开始拿版本号的过程可以看出,实际上这种方法就是hazard  pointer !! 看这篇:lock free数据结构内存回收技术-hazard pointer

读写开始拿可用版本号的过程:

1. 拿全局版本号V放入局部变量V0:    V0 <- V

2. 将V0写入全局数组中对应的槽位中:  A[slot] <- V0  // 这一步相当于将pointer给保护起来

3. 如果 V0 > recycleV 则可以开始安全的读写,版本号为V0,否则重试,跳第一步。 // 这一步相当于check一下上一步是否保护成功

测试结果

在2路超线程32core机器上,起32个线程,int64_t作为KV,TPS由原来的120W左右提升到240W左右,QPS由原来的680W左右提升到1900W左右。

Btree并发内存回收的更多相关文章

  1. 4、jvm内存回收——器

    内存回收---->垃圾回收---->GC GC 三基础,一个综合G1 串行:单线程,回收暂停其他 并行:多线程,回收暂停其他 并发:多线程,回收不暂停?! 成功好说,失败Serial Ol ...

  2. JVM内存回收机制简述

    JVM内存回收机制涉及的知识点太多了,了解越多越迷糊,汗一个,这里仅简单做个笔记,主要参考<深入理解Java虚拟机:JVM高级特性与最佳实践(第二版)> 目前java的jdk默认虚拟机为H ...

  3. Java内存回收机制

    在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方面工作都是由JVM自动完成的,降低了Java程序员的学习难度,避免了像C/C++直接操作内存的危险.但是,也正 ...

  4. Java的内存回收机制

    原文出处: cnblogs-小学徒V 在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方面工作都是由JVM自动完成的,降低了Java程序员的学习难度,避免了像C ...

  5. [转载]Java的内存回收机制

    转自:http://www.admin10000.com/document/1671.html 在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方面工作都是由J ...

  6. JVM中内存回收深入分析,各种垃圾收集器

    JVM启动有两种模式,client和server 一般JVM启动时会根据主机情况分析选择采用那种模式启动 可发现是server模式 JVM中尤其需要关注的就是HEAP堆区 堆区分为新生代和老年代 新生 ...

  7. 《深入理解Java虚拟机》学习笔记之内存回收

    垃圾收集(Garbage Collection,GC)并不是Java语言的半生产物,事实上GC历史远比Java久远,真正使用内存动态分配和垃圾收集技术的语言是诞生于1960年的Lisp语言.经过半个世 ...

  8. JVM(三)内存回收(一)

    最近花了相当长一段时间在看Hotspot JVM的GC和内存分配,本节先总结和回顾一下内存回收的相关知识点,内存的分配放到下节再讨论. 一.什么是JVM的GC GC即Garbage Collectio ...

  9. JVM(四)内存回收(二)

    在上一节中"JVM(三)内存回收(一)"我讲到了垃圾回收的几种算法,算是解决了之前提到的3个问题中的最后一个. 关于内存回收,还应该了解常用的内存回收器(GC Collector) ...

随机推荐

  1. 记一次Socket编程踩的坑

    闲来无事研究了下Socket,想用它做个简单的聊天室模型,结果踩了个坑,整半天才出来,惭愧啊,先上完成的代码吧 服务端: public partial class Form1 : Form { pub ...

  2. Android启动流程

    Android是一个基于Linux的开源操作系统.x86(x86是一系列的基于intel 8086 CPU的计算机微处理器指令集架构)是linux内核部署最常见的系统.然而,所有的Android设备都 ...

  3. CentOS7.4安装Java8

    官网下载Jdk8Linux64位版本: 使用MobaXterm工具连接远程Linux系统: 上传刚才下载好的文件到远程系统下 /usr/local/ 文件夹: 先进入压缩包所在目录再输入命令解压jdk ...

  4. Spark编程环境搭建(基于Intellij IDEA的Ultimate版本)(包含Java和Scala版的WordCount)(博主强烈推荐)

    福利 => 每天都推送 欢迎大家,关注微信扫码并加入我的4个微信公众号:   大数据躺过的坑      Java从入门到架构师      人工智能躺过的坑         Java全栈大联盟   ...

  5. 安装scrapy报错 error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools

    报错内容:Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools" ...

  6. elasticsearch(一):安装与配置

    一.elastic search的安装与配置 1.安装Java 并且配置JAVA_HOME环境变量. 2.下载elasticsearch,下载地址:https://www.elastic.co/dow ...

  7. [PY3]——threading.Event

    Class Event { __init__(self) clear(self) is_set(self) set(self) wait(self,timeout=None) } is_set(sel ...

  8. c#基础学习(0719)之异常处理

    异常处理的一般代码模式 try { //可能发生异常的代码 //当try中某行代码发生异常后,从该行代码开始,后面的代码都不会继续执行, //程序直接跳转到catch块中进行执行 } catch (E ...

  9. jQuery如何根据元素值删除数组元素

    用到的方法$.inArry(); $.inArray( value, array [, fromIndex ] ) value 任意类型 用于查找的值. array Array类型 指定被查找的数组. ...

  10. Json.Net组件指定/忽略序列化字段属性技巧知识点

    我们在用Json.Net序列化组件序列化类的时候,经常有这样的一个需求:指定被序列化类中的某些字段属性是要忽略的,或者是指定字段属性序列化 比如下面这个类: public class Bar { pu ...