讲师中心 微信公众号
AI工具推荐 视频效率加速

C++实现简单的内存垃圾回收器 _ 标记-清除算法模拟【源码】

陌伟酱_3589

陌伟酱_3589

发布时间:2026-04-10 22:07:55

|

247人浏览过

|

来源于php中文网

原创

C++中std::shared_ptr不能实现真正垃圾回收,因其仅为引用计数机制,无法处理循环引用,且未实现标记-清除所需的全局可达性分析、对象图遍历与统一回收流程。

c++实现简单的内存垃圾回收器 _ 标记-清除算法模拟【源码】

为什么 C++ 里不能直接用 std::shared_ptr 就算“实现垃圾回收”?

因为 shared_ptr 是引用计数,不是标记-清除;它无法处理循环引用,也不模拟“全局扫描堆内存→标记存活对象→统一清除未标记区域”这一过程。真要模拟标记-清除,就得自己管理一块堆内存、构造对象图、实现可达性分析——否则只是借了智能指针的壳,没碰核心逻辑。

实操建议:

  • 别在 new 出来的原始地址上直接套 shared_ptr 后就宣称“我写了 GC”,这绕过了所有标记逻辑
  • 真正模拟需手动维护一个 std::vector<void></void> 或自定义 ObjectHeader 链表,记录所有分配对象的起始地址和大小
  • 所有对象必须能被遍历字段(即:要么是固定结构体,要么带类型信息 + 字段偏移表),否则无法递归标记

怎么让普通 C++ 对象支持“被标记”?

得在对象内存布局前端插入元数据,比如一个 bool marked 标志位。但 C++ 没有运行时对象头,所以必须控制分配路径——所有对象都走自定义 allocate(),并在返回地址前写入头信息。

示例关键片段:

立即学习“C++免费学习笔记(深入)”;

struct ObjectHeader {
    bool marked;
    size_t size;
};
<p>void<em> MyGC::allocate(size_t size) {
void</em> raw = malloc(sizeof(ObjectHeader) + size);
ObjectHeader<em> header = static_cast<ObjectHeader</em>>(raw);
header->marked = false;
header->size = size;
// 把 header 地址存入 roots 或 objects 列表
objects.push_back(header);
return static_cast<char*>(raw) + sizeof(ObjectHeader);
}

注意:objects 必须是全局可访问的容器(如静态成员),否则标记阶段找不到待扫描对象;且分配后不能让外部用 delete,否则破坏管理一致性。

标记阶段怎么找到“根对象”并递归遍历?

根对象只能是栈变量、全局变量或寄存器中显式持有的指针。C++ 没有栈帧反射,所以必须人工注册——比如提供 MyGC::addRoot(void*),由用户在创建对象后立刻调用。

C++ 算法竞赛自动化测试数据生成与校验框架
C++ 算法竞赛自动化测试数据生成与校验框架

根据原题生成新题面、验证器及完整测试数据,自动套用 testlib 模板,用于用户要求生成测试数据时。

下载

常见错误现象:

  • 忘记注册根,导致整个对象图被误清
  • 注册了栈上临时变量地址(如 &x),后续函数返回后该地址失效,标记时读越界
  • 字段偏移硬编码出错,比如结构体有虚函数表指针,sizeof(A) 不等于字段总和,导致跳过真实指针字段

安全做法:只允许注册指向堆分配对象的指针(即 allocate() 返回值),并在标记时用固定偏移检查每个字段是否落在 objects 管理范围内。

清除阶段为什么不能直接 free() 所有未标记块?

因为 free() 只接受 malloc() 返回的原始地址,而你的对象指针是加了 sizeof(ObjectHeader) 偏移后的地址。直接传进去会崩溃。

正确做法:

  • 遍历 objects 容器,对每个 ObjectHeader* 判断 !header->marked
  • 调用 free(header)(不是 free(header + 1))
  • 从 objects 中移除该元素(避免后续重复清除或标记访问已释放内存)

性能影响:清除本身很快,但标记阶段若对象图深或宽,递归容易爆栈;建议改用显式栈(std::stack<void*>)替代递归函数调用。

兼容性提醒:这种模拟只适用于 POD 类型或明确知道字段布局的类;含 std::string、std::vector 的对象不能直接纳入,因为它们内部指针不在你可控字段范围内。

热门AI工具

更多
DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

5439

2023.08.02

全局变量怎么定义
全局变量怎么定义

本专题整合了全局变量相关内容,阅读专题下面的文章了解更多详细内容。

3885

2025.09.18

python 全局变量
python 全局变量

本专题整合了python中全局变量定义相关教程,阅读专题下面的文章了解更多详细内容。

1590

2025.09.18

c++ 全局变量
c++ 全局变量

本专题整合了c++全局变量的使用、定义、作用域等等内容,阅读专题下面的文章了解更多详细内容。

199

2026.03.17

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

4134

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

4271

2025.07.04

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

616

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

351

2025.11.27

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

0

2026.09.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Conan 2 Essentials 免费课程
Conan 2 Essentials 免费课程

共0课时 | 0人学习

CMake 与 Conan 集成实践
CMake 与 Conan 集成实践

共0课时 | 0人学习

Conan 2 高级依赖模型介绍
Conan 2 高级依赖模型介绍

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn