Datenlord |垃圾回收机制与无锁化编程(二)

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

作者 | 王璞

上一篇文章介绍了无锁化编程场景下的一种垃圾回收机制,Epoch-based Memory Reclaimation(EB)。 本篇介绍另一种无锁化编程场景下的垃圾回收机制,Hazard Pointer(HP)。HP也是一种确定型GC

HP的内存回收方法比较简单: 对无锁化编程场景下的每个线程,需要显式标注出该线程要竞争访问的共享对象,即线程把要竞争访问的对象的指针标注为危险指针(Hazard Pointer), 访问结束后或取消标注该危险指针、或标注该危险指针指向的共享对象为待回收。 在回收内存时,HP判断每个待回收的共享对象是否可以安全回收,只需要检查该对象的指针是否正被某个线程标注为危险指针,如果没有就能回收,否则不能回收。 HP跟EB一样也是采用空间换时间的策略,并不是马上回收每个可以被回收的共享对象,而是批量回收,以减少内存回收对程序性能的影响。

确定型GC算法Hazard Pointer(HP)


继续沿用无锁化堆栈作为例子来展示HP的用法,然后再介绍HP的细节。

Hazard Pointer(HP)的用法

采用HP作为GC的无锁化堆栈的入栈和出栈操作实现如下所示。

struct Node {
    void* data;
    std::atomic< Node * > next;
};

std::atomic<Node *> top; // 栈顶
top.store( nullptr ); // 初始化栈顶为空指针

bool push( Node* new_node ) {
    // 线程本地的危险指针队列里新增一个危险指针
    HP * hazard_cur_top = LocalHP::new_hp();
    
    while ( true ) {
        Node * cur_top = top.load();

        // 标注当前栈顶指针cur_top为危险指针
        hazard_cur_top.set(cur_top);

        new_node->next.store( cur_top );
        
        // CAS调用修改栈顶
        if ( top.compare_exchange_weak(
                cur_top, new_node )) {
            break; // 修改栈顶成功
        }
    }

    // 入栈操作成功,取消标注cur_top为危险指针
    hazard_cur_top.set(nullptr);
    return true;
}

Node * pop() {
    // 线程本地的危险指针队列里新增两个危险指针
    HP * hazard_cur_top = LocalHP::new_hp();
    HP * hazard_next = LocalHP::new_hp();

    while ( true ) {
        Node * cur_top = top.load();
        if ( cur_top == nullptr ) {
            break; // 堆栈为空
        }

        // 标注当前栈顶指针cur_top为危险指针
        hazard_cur_top.set(cur_top);

        Node * next = cur_top->next.load();

        // 标注当前栈顶的下一节点指针next为危险指针
        hazard_next.set(cur_top);
        
        // CAS调用修改栈顶
        if ( top.compare_exchange_weak(
                cur_top, next )) {
            break; // 修改栈顶成功
        }
    }

    if ( cur_top != nullptr ) {
        // 出栈操作成功,标注cur_top为待回收对象
        hazard_cur_top.maybe_reclaim();
    } else {
        // 栈为空,取消标注cur_top为危险指针
        hazard_cur_top.set(nullptr);
    }

    // 出栈操作结束,取消标注next为危险指针
    hazard_next.set(nullptr);

    return cur_top;
}

上面的实现可以看出,入栈操作和出栈操作有不同的危险指针。对于入栈操作:

  • 入栈操作只修改堆栈的当前栈顶指针,因此只需要标注一个危险指针,即当前栈顶为危险指针;
  • 入栈操作里每次循环,会更新栈顶指针,同时也要更新危险指针,即只把最新的栈顶指针标注为危险指针;
  • 入栈操作成功后,取消标注该危险指针,因为原栈顶节点还在堆栈里,不能被回收,即入栈操作不产生待回收对象。

对于出栈操作:

  • 出栈操作会修改堆栈的当前栈顶指针以及栈顶的下一节点指针,因此要标注两个危险指针;
  • 出栈操作里每次循环,要更新栈顶指针和栈顶的下一节点指针,同时也要更新对应的两个危险指针;
  • 如果出栈操作失败,即堆栈为空,则取消标注这两个危险指针;
  • 如果出栈操作成功,要标注当前栈顶指针为待回收对象,并取消标注栈顶的下一节点指针为危险指针。

HP如何安全回收内存?

为什么HP能保证安全回收内存呢?这里我们只考虑出栈操作,因为入栈操作不涉及内存回收。

假定有两个线程A和B同时调用无锁化堆栈的出栈操作,这两个线程都读取了当前栈顶指针cur_top, 这时线程A被抢占导致休眠,线程B继续执行出栈操作并成功取出cur_top指向的栈顶节点。 线程B由于成功执行出栈操作而标注cur_top指向的原栈顶节点为待回收对象,但是此时尚不能回收cur_top, 因为线程A还未执行完毕出栈操作,即线程A标注cur_top为危险指针。 只有等线程A恢复执行后,发现cur_top已经不是最新的栈顶指针,更新了栈顶指针并更新了对应的危险指针之后,才能安全回收原栈顶节点的内存。

由此可见,HP判断共享对象是否可回收的方法和上一篇Blog里介绍的EB不一样。 EB是标注出每个线程对共享对象的访问阶段,有点像是标注出临界区,不同的访问阶段产生不同的待回收对象指针,然后回收处于最老阶段的待回收对象的内存; HP是标注出每个线程要修改的共享对象的指针,而不是标注出临界区,因此HP标注的粒度更细。 但是HP和EB的回收策略相似,都是批量回收。

Hazard Pointer(HP)的实现

HP的实现比较直观,简单描述下HP的实现机制:

  • 每个线程维护一个本地队列LocalHP,用于保存线程本地标注的危险指针;
  • 再维护一个全局队列GlobalHP,用于保存待回收对象指针;
  • 每次线程调用maybe_reclaim()标注某个危险指针为待回收对象时,把该待回收对象从本地队列推送到全局队列;
  • 如果调用maybe_reclaim()推送待回收对象时,发现全局队列已满,则触发内存回收, 即逐个检查全局队列里每个待回收对象是否可回收,同时回收可回收对象的内存并从全局队列中删除之。

可见HP也是确定型GC,内存回收发生在调用maybe_reclaim()且全局队列已满时。此外,LocalHPGlobalHP要采用无锁化队列,来保证HP的实现也是无锁的。

HP虽然比较简单直观,但是HP在内存回收时的开销比EB大, 因为HP要逐个判断全局队列里每个待回收对象是否可回收,即检查每个待回收对象的指针是否有被某个线程正标注为危险指针, 如果待回收对象比较多,而且线程也比较多,那检查工作量会比较大; 而EB在回收内存时,一股脑回收处于最老阶段的所有待回收对象,每个待回收对象在回收前已经关联了其产生的阶段,回收时无需挨个检查每个待回收对象。

libcds里已经有HP的C++实现,开发者无需自行实现HP。

使用Epoch-Based Reclamation(EBR,特定域回收的一种方法)修改 lazy-list 如前文所说,lazy-list最大的隐患莫过于逻辑删除,而没有物理删除问题,因此EBR首先就把这个问题给他solve了。 一.EBR修改部分 int parse_delete(intset_l_t *set, val_t val) { node_l_t *pred, *curr; int result, validated, isVal; while(1) { //Init pred = set->head; curr = get 阅读详情

相关推荐

Datenlord | Rust 无编程之Crossbeam Epoch算法解析

作者 | 施继成 转自《Rust Magazine中文精选》上次的文章介绍了无数据结构的内存管理机制 EBR,该机制相较于其他的内存管理机制具有更高的执行效率。然而由于理念的复杂性,EBR 的实现并不容易,为每一个无数据结构从头实现 EBR 也无必要,因此很自然得大家会考虑将 EBR 的核心理念 epoch 抽取出来变成库,让大家能够复用。Crossbeam-epoch 是一套成熟的被大家广泛使用的 EBR 库,本文将从实现原理部分进行较为详细的解析,并且在此过程中进行。如前文所述,大家一般在和Cros

DatenLord的博客 491

云原生 分布式 存储 基石 etcd 解析

云原生 分布式 存储 基石 etcd 解析 解压密码 123

使用QSBR进行安全的内存回收

使用QSBR进行安全的内存回收在多线程场景下,经常我们需要并发访问一个数据结构,为了保证线程安全我们会考虑使用互斥设施来进行同步,更进一步我们会根据对这个数据结构的读写比例而选用读写进行优。但是读写不是唯一的方式,我们可以借助于COW技术来做到写操作不需要加,也就是在读的时候正常读,写的时候,先加拷贝一份,然后进行写,写完就原子的更新回去,使用COW实现避免了频繁加读写本身的性能开销。读

zyfforlinux 2893

上篇 | 说说无(Lock-Free)编程那些事

1. 引言现代计算机,即使很小的智能机亦或者平板电脑,都是一个多核(多CPU)处理设备,如何充分利用多核CPU资源,以达到单机性能的极大成为我们码农进行软件开发的痛点和...

腾讯技术工程 1495

共享数据结构无释放之epoch-based reclame

epoch based reclaim

legend050709的专栏 821

Epoch Based Reclamation 的个人理解

目录缘起什么是Epoch Based Reclamation为什么要Epoch Based ReclamationEpoch Based Reclamation的原理概述来个小例子后记 缘起 最近在看大佬视频,在用rust实现一个concurrenthashmap的时候,用到了crossbeam中epoch,顿时一阵懵逼,囊碟括咧(这啥玩意啊)?于是便开始启动搜索引擎大发,再整合诸多信息后,有了此偏小记。 什么是Epoch Based Reclamation 大概意思上来说,这是无编程模式下的一种内存管理

lucyTheSlayer的博客 1659

Smart Pointers(智能指针)Epoch-Based Reclamation (EBR)详细介绍和对比

Smart Pointers 它基于三个概念,包括堆栈分配的指向堆分配内存的指针,扮演每个对象的垃圾收集角色和 RAII。 有 3 个 API:unique_ptr、shared_ptr 和 weak_ptr。 1)unique_ptr 只允许底层指针的一个所有者,但不支持复制,但支持移动语义。 然后,当所有者指针超出范围时,它的内存被回收。 因此,它不需要显式调用“删除”。 更重要的是,unique_ptr 可以自我记录并捕获编程错误。 尽管如此,它通常需要复制指针。 unique_ptr 只允许底层指

weixin_42455006的博客 777

Datenlord |垃圾回收机制编程(Garbage Collection and Lock-Free Programming)

作者 | 王璞垃圾回收机制(GC)对大部分开发者来说应该不陌生,特别是Java开发者或多或少都跟GC打过交道。 GC的优点是实现对堆上分配的内存动态回收,避免内存泄漏。但是GC的缺点是对性能有一定影响,特别是stop the world问题, 而且GC什么时候回收内存是不确定的,开发者无法知晓。在无编程场景下,用Java这种有GC的语言,一定程度简了对内存的管理,降低了无编程的难度。 无编程,顾名思义,就是不用。无编程特指在多线程编程的时候,对线程间共享数据的并发修改不使用, 而采用基

DatenLord的博客 288

【crossbeam系列】2 crossbeam-epoch:基于epoch的无垃圾收集”

上次我们试图实现一个无的并发栈,但是发现由于Rust没有GC,简单的实现会导致内存泄漏。于是crossbeam提供了一个基于epoch的“垃圾收集”(epoch based recla...

Rust语言学习交流 1003

C++多线程 原子操作编程

编程真的很难,如果要完全写对那就成了变态难,出错了平时常见的调试手段根本没用,几乎全靠脑补;并且这种轮询方式相对于的中断挂起方式来讲,只有在超高并发的前提下才能达到一个理想的效果,低并发下空载会对系统资源造成极大的浪费,因此原则上我不推崇这个玩意;从测试结果来看,所有平台上自旋性能都非常接近无实现,并且其使用方式和互斥几乎没差别,因此在没啃透之前,使用的方式才是明智的选择.LinuxC/C++服务器开发/架构师教程: https://ke.qq.com/course/417774?

C/C++Linux、音视频、DPDK 1102

c#尝试写入或者读取受保护的内存_SEBR:多线程内存回收方案(0)

在此介绍我自己的内存回收方案(SEBR),它使用了C++17,作为并发环境下的一种Safe Memory Reclamation,它相对于经典的 Epoch based reclamation(5.2.3)和其他具体的实现方案有些明显的差别。实现原理:怎样理解 Epoch based reclaimation?​www.zhihu.com我把它称为Scalable Epoch Based Recl...

weixin_39525933的博客 417

LeanStore论文分析

目录 摘要: 构筑BLock: 指针移动 高效的页面替换: 线程同步的扩展能力: LEANSTORE 数据结构概述 Swizzling的详细设计 Cooling Stage 输入输出 缓冲区管理的数据结构 乐观 Epoch-Based Reclamation 内存分配和NUMA友好的 实现细节和优方案 汇总: 摘要: 论文认为,传统的采用BufferP...

duxingxia356的专栏 968

DatenLord|Curp 共识协议的重新思考

作者 | 施继成,达坦科技(DatenLord)目录共识简介Curp 共识协议Curp 协议总结和讨论共识协议是一种让分布式系统中多个节点保持信息一致的通信协议,即使少数节点发生故障也依然能够保证信息的准确和一致。而每当我们在讨论共识协议的时候往往会想到 classic paxos 或者 raft 协议,这两个协议是很多其他协议的基础,后续的很多协议都可以看成是它们的变种,例如 Multi-Paxos和 Fast-Paxos等等。我们今天先从这两个协议入手,先来回顾一下这两个协议是如何工作的。首先来看 cl

DatenLord的博客 551

DatenLord前沿技术分享 No.31

专注下一代云计算——“天空计算”的基础设施技术,致力于拓宽云计算的边界。达坦科技打造的新一代开源跨云存储平台DatenLord,通过软硬件深度融合的方式打通云间壁垒,实现数据高效跨云访问,建立海量异地、异构数据的统一存储访问机制,为云上应用提供高性能安全存储支持。达坦科技专注于打造新一代开源跨云存储平台DatenLord,通过软硬件深度融合的方式打通云云壁垒,致力于解决多云架构、多数据中心场景下异构存储、数据统一管理需求等问题,以满足不同行业客户对海量数据跨云、跨数据中心高性能访问的需求。

DatenLord的博客 262

Polars Parquet 读写扫描实战指南:从 `read_parquet` 到惰性查询优

> 本指南对应仓库 `docs/source/user-guide/io/parquet.md`,以 Python / Rust 双语言示例贯穿 Parquet 的读取、写入惰性扫描三大主题。Polars 以 Rust 实现的高性能列式引擎,其内存中的 `DataFrame` 布局磁盘上的 Parquet 列式文件布局高度相似,因此读写 Parquet 非常高效。读完本文,你将掌握 `read

gitblog_00051的博客 587

DatenLord前沿技术分享 No.12

OPAE-Xilinx平台级复用开源项目介绍

DatenLord的博客 301

DatenLord前沿技术分享 No.13

异步事件驱动的电路机制 & 基于RISC-V的全异步超标量CPU体系结构

DatenLord的博客 210

SkillSpector行为AST分析:8种代码行为模式检测

SkillSpector是一款AI代理技能安全扫描工具,能够检测AI代理技能中的漏洞、恶意模式和安全风险。其中,行为AST分析作为核心功能之一,通过解析Python代码的抽象语法树(AST)来识别危险的执行模式,为AI代理技能提供全面的安全保障。 ## 行为AST分析的核心功能 行为AST分析模块通过静态代码分析技术,深入检查Python代码中的潜在安全风险。该模块位于[src/skillsp

gitblog_00020的博客 989

SkillSpector工具滥用检测:3种工具链攻击模式

SkillSpector是一款专业的AI代理技能安全扫描工具,能够有效检测AI技能中的漏洞、恶意模式和安全风险。在AI代理工具链日益复杂的今天,工具滥用已成为主要安全威胁之一。本文将深入解析SkillSpector如何识别三种最危险的工具链攻击模式,帮助开发者构建更安全的AI应用。 ## 工具参数滥用(TM1):危险参数的隐蔽威胁 工具参数滥用是最常见也最容易被忽视的攻击向量。SkillSpe

gitblog_00400的博客 506
上一篇: 万字长文,详述TRIDENT: Poseidon 哈希算法的硬件加速与实现!
下一篇: DatenLord|Rust for Linux 要来了,这对我们意味着什么
达坦科技DatenLord
博客等级 码龄4年 529粉丝 159原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

达坦科技DatenLord

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值