C++标准不带增量式GC因其“不为不用的功能付费”哲学,增量GC需写屏障、记忆集等开销,与嵌入式/实时场景冲突;标准采用RAII+智能指针显式管理。

为什么 C++ 标准不带增量式 GC
因为 C++ 的设计哲学是“不为不用的功能付费”,而增量式 GC 意味着运行时必须插入写屏障、维护记忆集、分阶段扫描——这些开销对多数 C++ 程序(尤其是嵌入式、实时或性能敏感场景)不可接受。标准库选择 RAII + unique_ptr/shared_ptr 显式管理,把控制权交给程序员。
写屏障怎么插进赋值操作里
增量 GC 必须捕获“对象字段被修改”这个事件,否则新引用可能漏扫。C++ 没有语言级钩子,只能靠封装指针类型来拦截。常见做法是用自定义智能指针包装原始指针,在 operator= 和 operator-> 里触发写屏障。
- 不能直接重载内置指针的赋值,所以所有可被 GC 管理的对象字段,必须声明为
GcPtr<t></t>而非T* - 写屏障逻辑通常只是把被修改的字段所在对象加入“脏页”或“记忆集”,例如:
remember_write(this, &field) - 注意:
memcpy、memset、结构体整体赋值会绕过屏障,必须禁止或替换为带屏障的版本
如何把标记-清除拆成小步执行
核心是把原本一口气扫完的根集合和对象图遍历,切成固定时间片(比如 0.5ms)的小任务,每次只处理一部分,中间保存上下文(当前扫描栈、迭代器位置等)。
- 根集合扫描通常很快,可一次做完;慢的是对象图遍历,需用显式栈代替递归调用栈
- 每轮只从栈顶弹出 N 个对象(如 16 个),标记它们的子对象并压栈,然后 yield
- 必须在安全点(safepoint)暂停 mutator(即你的业务线程),常见做法是在分配内存前检查是否该 yield,或用信号/原子标志位协作
-
std::atomic<bool> gc_should_yield</bool>是常用同步手段,但要注意编译器重排,需配memory_order_acquire
对象布局和内存分配怎么配合 GC
GC 需要快速识别对象头、类型信息、大小,以及判断某地址是否指向有效对象。C++ 没有运行时类型反射,得自己编码进去。
立即学习“C++免费学习笔记(深入)”;
- 每个 GC 对象前加固定头:至少包含
mark_bit(1 bit)、size(4–8 字节)、vptr_or_type_id(用于后续析构) - 分配必须走 GC 自己的
gc_malloc(),不能混用new或malloc,否则扫描时无法区分合法对象和垃圾字节 - 内存池建议按大小分级(如 16B/32B/64B…),避免碎片;大对象单独链表管理,减少扫描压力
- 注意:
placement new构造对象后,必须确保头信息已就位且未被覆盖










