Btree并发内存回收
在并发写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并发内存回收的更多相关文章
- 4、jvm内存回收——器
内存回收---->垃圾回收---->GC GC 三基础,一个综合G1 串行:单线程,回收暂停其他 并行:多线程,回收暂停其他 并发:多线程,回收不暂停?! 成功好说,失败Serial Ol ...
- JVM内存回收机制简述
JVM内存回收机制涉及的知识点太多了,了解越多越迷糊,汗一个,这里仅简单做个笔记,主要参考<深入理解Java虚拟机:JVM高级特性与最佳实践(第二版)> 目前java的jdk默认虚拟机为H ...
- Java内存回收机制
在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方面工作都是由JVM自动完成的,降低了Java程序员的学习难度,避免了像C/C++直接操作内存的危险.但是,也正 ...
- Java的内存回收机制
原文出处: cnblogs-小学徒V 在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方面工作都是由JVM自动完成的,降低了Java程序员的学习难度,避免了像C ...
- [转载]Java的内存回收机制
转自:http://www.admin10000.com/document/1671.html 在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方面工作都是由J ...
- JVM中内存回收深入分析,各种垃圾收集器
JVM启动有两种模式,client和server 一般JVM启动时会根据主机情况分析选择采用那种模式启动 可发现是server模式 JVM中尤其需要关注的就是HEAP堆区 堆区分为新生代和老年代 新生 ...
- 《深入理解Java虚拟机》学习笔记之内存回收
垃圾收集(Garbage Collection,GC)并不是Java语言的半生产物,事实上GC历史远比Java久远,真正使用内存动态分配和垃圾收集技术的语言是诞生于1960年的Lisp语言.经过半个世 ...
- JVM(三)内存回收(一)
最近花了相当长一段时间在看Hotspot JVM的GC和内存分配,本节先总结和回顾一下内存回收的相关知识点,内存的分配放到下节再讨论. 一.什么是JVM的GC GC即Garbage Collectio ...
- JVM(四)内存回收(二)
在上一节中"JVM(三)内存回收(一)"我讲到了垃圾回收的几种算法,算是解决了之前提到的3个问题中的最后一个. 关于内存回收,还应该了解常用的内存回收器(GC Collector) ...
随机推荐
- 《Mysql技术内幕,Innodb存储引擎》——文件、表
文件 日志 错误日志 对Mysql启动.运行和关闭过程进行记录,通过SHOW VARIABLES LIKE 'log_error'查看日志文件位置. 慢查询日志 Mysql启动时设置一个阈值,运行时间 ...
- OpenDaylight虚拟租户网络(VTN)详解及开发环境搭建
一.VTN简介及架构分析: 具体详见开发人员指南wiki:https://wiki.opendaylight.org/view/Release/Lithium/VTN/developer_Guid ...
- java-jdk7-forkjoin带有返回值
来自并发编程网: http://ifeve.com/fork-join-3/ 如果这个任务必须解决一个超过预定义大小的问题,你应该将这个任务分解成更多的子任务,并且用Fork/Join框架来执行这些子 ...
- redis-手写redis切片和非切片连接池并注入springboot中
spring-data整合了redispool, 并提供redisTemplate使用, 但有时需要用到shradedJedisPool, 就需要手动注入了 手写redispool并注入springb ...
- 利用Google翻译成多国语言的见解
1.首先注意,英语句子中的 第一个单词的首字母要大写, 2.句子结尾了,要用句号. 3.英语中单词和前面的标点符号要留一个空格,如: you.Are 应该是 you. Are you..... ...
- Nginx配置日志格式记录cookie
Nginx配置日志格式记录cookie1. 一般用来做UV统计,或者获取用户token等. 配置方式: 在nginx的配置文件中有个变量:$http_cookie来获取cookie的信息.配置方式很 ...
- Eclipse编辑JS响应慢,复制粘贴卡顿的解决方案
我使用的是官网标准版的eclipse4.3 for javaEE, 写JS时卡的简直无法忍受,尝试去掉所有的validate,包括菜单和项目属性中的都没作用. 整个人都急疯了~ 后来在项目根目录.pr ...
- C语言——打印“Hello World!”,这么简单?
打印Hello World! #inculde <stdio.h> int main(){ printf("Hello World!"); return 0; } 第一 ...
- angular 下载文件
后台直接返回文件即可,以wcf为例:读取目录文件返回,或直接生成文件并返回. public Stream GetCodeSn() { return File.OpenRead("e:\\工作 ...
- [转]How to Create an Add-in for Microsoft Outlook
本文转自:https://www.codeproject.com/Articles/1112815/How-to-Create-an-Add-in-for-Microsoft-Outlook This ...