当前位置: 首页 > news >正文

深入对比:Chromium的base::RefCounted与std::shared_ptr——从线程安全到性能优化

1. 引言:为什么需要对比两者?

在现代C++开发中,引用计数(Reference Counting)是一种常见的内存管理技术,用于自动管理对象的生命周期。C++标准库提供了std::shared_ptr作为通用的引用计数智能指针,而Chromium项目则自定义了一套引用计数系统(base::RefCountedbase::RefCountedThreadSafe)。

为什么Chromium不直接使用std::shared_ptr

  1. 性能优化:Chromium对内存和CPU效率要求极高,std::shared_ptr的全局原子操作可能带来不必要的开销。

  2. 线程模型控制:Chromium的线程模型(如UI线程、IO线程、GPU线程)需要更细粒度的线程安全控制。

  3. 内存占用优化std::shared_ptr的控制块(Control Block)会增加额外内存开销,而Chromium的base::RefCounted将引用计数直接嵌入对象,减少内存占用。

本文将深入对比base::RefCounted(Chromium风格)和std::shared_ptr(C++标准库)的设计差异,涵盖线程安全、性能优化、内存模型、功能扩展等方面,帮助开发者理解它们的适用场景。


2. 线程安全性:最关键的差异

2.1 base::RefCountedThreadSafe(线程安全版本)

Chromium提供了两种引用计数模板:

  • base::RefCounted(非线程安全,引用计数无原子保护)

  • base::RefCountedThreadSafe(线程安全,引用计数使用原子操作)

关键特点
  1. 仅保证引用计数的原子性

    • AddRef()Release()使用std::atomic保证线程安全。

    • 对象的析构仍需开发者控制(通常需回到创建线程析构,避免竞态条件)。

  2. 适用于跨线程共享对象

    • 例如网络模块、异步任务等需要多线程访问的场景。

示例代码
class NetworkResource : public base::RefCountedThreadSafe<NetworkResource> {public:void Fetch() { /* 跨线程操作 */ }
};// 跨线程传递
scoped_refptr<NetworkResource> resource = base::MakeRefCounted<NetworkResource>();
io_thread.PostTask(FROM_HERE, base::BindOnce(&NetworkResource::Fetch, resource));

2.2 std::shared_ptr(全局线程安全)

std::shared_ptr的设计目标是通用性,因此它的线程安全策略更加严格:

  1. 控制块(Control Block)完全线程安全

    • 强引用计数、弱引用计数、删除器(Deleter)均使用原子操作。

  2. 对象析构可能发生在任意线程

    • 最后一个shared_ptr释放时,对象析构可能不在创建线程(可能影响线程局部存储)。

示例代码
auto resource = std::make_shared<NetworkResource>();
std::thread([resource] { resource->Fetch();  // 可能在任何线程析构
}).detach();

2.3 对比总结

维度base::RefCountedThreadSafestd::shared_ptr
引用计数原子性AddRef/Release原子控制块全原子(强/弱引用+删除器)
析构线程安全性需手动控制(如PostTask回原线程)可在任意线程析构
适用场景需要精细线程控制的系统(如浏览器)通用跨平台代码

关键结论

  • 如果需要在特定线程析构对象(如UI线程),base::RefCountedThreadSafe更合适。

  • 如果希望自动管理析构线程std::shared_ptr更方便,但可能牺牲性能。


3. 性能优化:内存与原子操作开销

3.1 内存占用对比

实现方式base::RefCountedstd::shared_ptr
存储位置引用计数嵌入对象(无额外开销)额外控制块(多2个指针大小)
内存占用更小更大(除非使用make_shared优化)
std::shared_ptr的内存布局
+-------------------+      +-------------------+
| shared_ptr<T>     | ---> | Control Block      |
| - 对象指针        |      | - 强引用计数       |
+-------------------+      | - 弱引用计数       || - 删除器           || - 对象内存(可选) |+-------------------+
  • 如果使用new T + shared_ptr,对象和控制块分开分配(2次内存分配)。

  • 如果使用make_shared,对象和控制块可能合并(1次分配)。

base::RefCounted的内存布局
+-------------------+
| RefCounted<T>     |
| - ref_count_      |  // 引用计数直接存储在对象内
| - 其他成员        |
+-------------------+
  • 无额外控制块,内存更紧凑。


3.2 原子操作开销

操作base::RefCounted(非线程安全)base::RefCountedThreadSafestd::shared_ptr
引用计数增减普通整数操作(无锁)原子操作(std::atomic原子操作
适用场景单线程(如UI线程)多线程共享通用多线程

性能影响

  • 原子操作(如fetch_add)比普通操作慢约2-10倍(取决于CPU架构)。

  • Chromium的base::RefCounted允许在单线程场景禁用原子操作,提升性能。


4. 功能扩展:弱引用与循环引用处理

4.1 std::shared_ptrweak_ptr机制

std::weak_ptr用于解决循环引用问题,例如:

struct Node {std::shared_ptr<Node> next;std::weak_ptr<Node> prev;  // 避免循环引用
};
  • 优点:自动检测对象是否存活,避免内存泄漏。

  • 缺点:控制块需额外维护弱引用计数,增加内存开销。

4.2 Chromium的替代方案

Chromium未内置弱引用,但提供了base::WeakPtrFactory

class Document : public base::RefCounted<Document> {public:base::WeakPtr<Document> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }private:base::WeakPtrFactory<Document> weak_factory_{this};
};
  • 缺点:需手动管理,不如weak_ptr方便。

  • 优点:无额外控制块,内存更高效。


5. 设计哲学:为何Chromium选择自定义实现?

  1. 极致性能优化

    • 浏览器对内存和CPU敏感,std::shared_ptr的通用性带来额外开销。

  2. 明确线程模型

    • Chromium的线程任务需精细控制(如UI线程仅允许特定操作)。

  3. 减少依赖

    • 自定义实现避免受标准库实现的限制(如ABI兼容性问题)。


6. 最佳实践:如何选择?

场景推荐方案
高性能单线程对象base::RefCounted
跨线程共享对象base::RefCountedThreadSafe
通用代码,需弱引用支持std::shared_ptr + weak_ptr
内存敏感型应用Chromium风格(无控制块开销)

7. 总结

维度base::RefCountedstd::shared_ptr
线程安全可定制(安全/非安全)全局原子(可能性能损失)
内存开销更小(无控制块)更大(控制块+弱引用)
适用场景高性能、线程模型明确的系统通用代码、需要弱引用

最终建议

  • 如果是Chromium开发或高性能中间件,优先使用base::RefCounted

  • 如果是通用C++项目std::shared_ptr更方便。


延伸阅读

  • Chromium RefCounted源码分析

  • C++标准库shared_ptr实现

http://www.dtcms.com/a/337456.html

相关文章:

  • 宏观认识 Unitree LiDAR L1 及其在自动驾驶中的应用
  • Avalonia实例实战七:动画
  • win环境断电强制拉库报ORA-600 kcbzib_kcrsds_1故障处理---惜分飞
  • 数据库:表和索引结构
  • 【datawhale组队学习】n8n TASK01
  • 知识蒸馏 Jensen-Shannon散度
  • 为什么品牌更愿意为新品打广告?
  • 机器学习——XGBoost算法
  • 力扣2道dp
  • easyexcel fastexcel 官方文档 easyexcel合并单元格
  • 项目会议怎么开才有效
  • 【前端面试题】JavaScript 核心知识点解析(第十四题解析到第二十二题)
  • 【Mac】【Minecraft】关于如何在Mac上搭建基岩版MC服务器的方法
  • 前端Element-plus的选择器 el-select 清空内容时,后端对应的更新方式,支持更新为null
  • 【秋招笔试】2025.08.09网易秋招机考
  • openEuler系统备份与恢复方法
  • 【DL学习笔记】交叉熵损失函数详解
  • 无人机视角违章建筑识别分割数据集labelme格式343张2类别
  • AI测试自愈率80%!语音编程提效10倍——2025软件AI化爆炸级突破
  • 端面试题大汇总二
  • 分库分表数据源如何清洗同步到目标表
  • 大数据计算引擎(二)——Flink
  • 大数据计算引擎(四)—— Impala
  • 【matlab】考虑源荷不平衡的微电网鲁棒定价研究
  • Pandas 数据导入导出、索引、分组聚合与可视化
  • (第十八期)图像标签的三个常用属性:width、height、border
  • 特赞内容运营解决方案,AI重构品牌内容价值链
  • 云计算学习100天-第21天
  • 整体设计 之“凝聚式中心点”原型 --整除:智能合约和DBMS的深层融合 之2
  • 将 iPhone 联系人转移到 Infinix 的完整指南