news 2026/6/15 14:34:53

《Unreal 对 C++ 做了什么》系列 10. 垃圾回收器 (GC) 原理:标记-清除与 GC 根对象

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《Unreal 对 C++ 做了什么》系列 10. 垃圾回收器 (GC) 原理:标记-清除与 GC 根对象

《Unreal 对 C++ 做了什么》系列 (10/54)

10. 垃圾回收器 (GC) 原理:标记-清除与 GC 根对象 🧹

🚀 导言:为什么 C++ 需要垃圾回收?

在传统的 C++ 开发中,“谁申请,谁释放”是金科玉律。但在拥有数万个相互引用对象的游戏引擎中,手动管理引用链几乎是不可能的。如果 Actor A 引用了数据 B,而 B 又引用了特效 C,手动删除 A 时,你必须确保 B 和 C 没有被其他地方引用。

虚幻引擎通过一套名为Mark and Sweep(标记-清除)的垃圾回收机制,解决了这个难题。


🔑 1. 核心算法:标记-清除 (Mark and Sweep)

UE 的 GC 并不是实时的(不像 C# 的分代回收),而是一种追踪式的回收。它分为两个主要阶段:

阶段 A:标记 (Marking)

GC 会从一组被称为Root Set(根集)的对象开始,顺着它们身上的UPROPERTY指针向下摸索。

  • 只要是能从“根”顺着指针摸到的对象,就被标记为“可达(Reachable)”。
  • 没被摸到的对象,就像是断开连接的孤岛,被标记为“不可达”。
阶段 B:清除 (Sweeping)

引擎遍历全局对象表(GUObjectArray),将所有没有“可达”标记的对象彻底从内存中抹除,并归还内存。


🔑 2. 什么是“根” (Root Set)?

如果所有对象都被 GC,那谁来保护第一批对象不被回收?答案就是Root Set
以下对象会自动进入根集,成为 GC 扫描的起点:

  • UGameEngine:引擎对象。
  • UWorld:当前的关卡及其所有的 Actor。
  • UGameInstance:贯穿游戏始终的全局对象。
  • 被标记为Root的对象:你可以通过AddToRoot()手动将一个对象固定在内存中(记得用RemoveFromRoot()释放,否则会内存泄漏)。

🔑 3. 为什么UPROPERTY如此重要?

这是初学者最容易崩溃的地方:GC 只认UPROPERTY

UCLASS()classAMyCharacter:publicACharacter{GENERATED_BODY()// 情况 1:GC 知道 Character 引用了 WeaponUPROPERTY()UWeapon*MyWeapon;// 情况 2:GC 彻底无视这个指针!UWeapon*HiddenWeapon;};
  • 情况 1:当 GC 扫描 Character 时,发现它有一个UPROPERTY指向MyWeapon。于是MyWeapon被标记为可达,幸免于难。
  • 情况 2:虽然你在 C++ 里赋值了HiddenWeapon,但 GC 扫描时看不见它。GC 会认为HiddenWeapon指向的对象没有人用,直接将其回收。此时HiddenWeapon就成了一个野指针,一旦访问立即崩溃。

🔑 4. 性能克星:GC 引起的卡顿 (Hitch)

GC 扫描几万个对象是需要时间的。在早期版本中,GC 会“冻结”整个游戏线程来执行扫描,这就是为什么老游戏有时会突然卡一下。

UE 对 C++ 做的优化:

  1. 并行标记 (Parallel Mark):利用多线程同时扫描不同的对象树。
  2. 增量清除 (Incremental Reachability Analysis):将扫描工作拆分到多帧完成,避免单帧耗时过长。
  3. 簇 (Clustering):将一些永远会在一起的对象(如某个 Actor 及其所有 Component)打包成一个“簇”。GC 只需要检查 Actor 是否可达,就可以直接判定整个簇的状态,极大提升速度。

📊 GC 运转流程表

步骤执行内容开发者职责
1. 注册对象创建时进入GUObjectArray使用NewObjectSpawnActor
2. 追踪GC 扫描UPROPERTY引用链务必给 UObject 指针加UPROPERTY()
3. 标记判定对象是否可达正常引用即可,特殊情况使用AddToRoot
4. 销毁释放不可达对象的内存确保没有非UPROPERTY指针指向它

⚠️ 避坑指南:如何与 GC 和谐共处?

  1. 容器保护:如果你用TArray<UObject*>存对象,这个数组也必须加UPROPERTY(),否则数组里的对象会被当成垃圾清理掉。
  2. **避免手动delete**:永远不要对UObject使用delete。如果你想让它消失,取消对它的引用,或者调用MarkAsGarbage()(UE5)。
  3. 静态变量风险:普通的static UObject* MyGlobalPtr无法被UPROPERTY标记。如果必须使用全局 UObject,请考虑将其放入UGameInstance中管理。

结语

垃圾回收是虚幻引擎为 C++ 开发者提供的“安全气囊”。它通过UPROPERTY引用链建立了一套自动化的生存法则。只要你遵循“凡是 UObject 指针必加宏”的原则,你就已经掌握了 UE 内存管理的一半真谛。


下一篇预告:《11. 弱引用与软引用:TWeakObjectPtr 和 TSoftObjectPtr》。我们将探讨:如果我不希望一个指针“保住”对象的命,或者我希望延迟加载对象,该怎么办?

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/15 13:17:49

Z-Image节日营销秘籍:云端百组模板快速出图

Z-Image节日营销秘籍&#xff1a;云端百组模板快速出图 1. 电商美工的节日作图痛点 每年618、双11等大促节点&#xff0c;电商美工都会面临海量的作图需求。传统工作模式下&#xff0c;一个中型店铺可能需要&#xff1a; 设计主图、详情页、海报、banner等10种素材类型为不同…

作者头像 李华
网站建设 2026/6/15 13:09:16

LRCGET歌词批量下载工具:快速入门与深度使用指南

LRCGET歌词批量下载工具&#xff1a;快速入门与深度使用指南 【免费下载链接】lrcget Utility for mass-downloading LRC synced lyrics for your offline music library. 项目地址: https://gitcode.com/gh_mirrors/lr/lrcget LRCGET是一款专为音乐爱好者设计的跨平台歌…

作者头像 李华
网站建设 2026/6/15 13:10:17

AI手势识别是否需要GPU?CPU版部署实战告诉你答案

AI手势识别是否需要GPU&#xff1f;CPU版部署实战告诉你答案 1. 引言&#xff1a;AI 手势识别与追踪的现实挑战 随着人机交互技术的不断演进&#xff0c;AI手势识别正逐步从实验室走向消费级应用。无论是智能穿戴设备、AR/VR交互&#xff0c;还是智能家居控制&#xff0c;精准…

作者头像 李华
网站建设 2026/6/13 14:21:45

毕业设计救星:骨骼点检测云端GPU,1小时1块不延期

毕业设计救星&#xff1a;骨骼点检测云端GPU&#xff0c;1小时1块不延期 引言&#xff1a;为什么你需要云端GPU做骨骼点检测&#xff1f; 作为一名大四学生&#xff0c;当你正在为毕业设计焦头烂额时&#xff0c;突然发现自己的笔记本电脑根本跑不动骨骼点检测模型&#xff0…

作者头像 李华
网站建设 2026/6/15 14:18:06

AI手势交互系统:MediaPipe Hands部署与调优

AI手势交互系统&#xff1a;MediaPipe Hands部署与调优 1. 引言&#xff1a;AI 手势识别与追踪的现实价值 随着人机交互技术的不断演进&#xff0c;非接触式控制正逐步成为智能设备的重要输入方式。从VR/AR中的虚拟操作&#xff0c;到智能家居的隔空控制&#xff0c;再到工业…

作者头像 李华
网站建设 2026/6/14 9:10:34

暗黑3终极自动化助手:一键配置快速上手指南

暗黑3终极自动化助手&#xff1a;一键配置快速上手指南 【免费下载链接】D3keyHelper D3KeyHelper是一个有图形界面&#xff0c;可自定义配置的暗黑3鼠标宏工具。 项目地址: https://gitcode.com/gh_mirrors/d3/D3keyHelper 还在为暗黑破坏神3中繁琐的技能连招和重复操作…

作者头像 李华