news 2026/4/15 19:48:19

《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/4/15 12:49:36

3分钟快速上手:Gamepad API Test 游戏手柄测试终极指南

3分钟快速上手&#xff1a;Gamepad API Test 游戏手柄测试终极指南 【免费下载链接】gamepadtest Gamepad API Test 项目地址: https://gitcode.com/gh_mirrors/ga/gamepadtest Gamepad API Test 是一款基于 JavaScript 开发的轻量级游戏手柄测试工具&#xff0c;专为检…

作者头像 李华
网站建设 2026/4/15 12:48:21

终极暗黑破坏神存档编辑器:从新手到专家的完整使用指南

终极暗黑破坏神存档编辑器&#xff1a;从新手到专家的完整使用指南 【免费下载链接】diablo_edit Diablo II Character editor. 项目地址: https://gitcode.com/gh_mirrors/di/diablo_edit 你是否曾经在暗黑破坏神II中为了一个完美装备刷了无数遍BOSS&#xff1f;是否因…

作者头像 李华
网站建设 2026/4/15 12:48:21

Windows系统苹果设备驱动自动化安装解决方案

Windows系统苹果设备驱动自动化安装解决方案 【免费下载链接】Apple-Mobile-Drivers-Installer Powershell script to easily install Apple USB and Mobile Device Ethernet (USB Tethering) drivers on Windows! 项目地址: https://gitcode.com/gh_mirrors/ap/Apple-Mobile…

作者头像 李华
网站建设 2026/4/15 12:48:23

Steam游戏清单高效获取方案:Onekey工具深度解析与实战指南

Steam游戏清单高效获取方案&#xff1a;Onekey工具深度解析与实战指南 【免费下载链接】Onekey Onekey Steam Depot Manifest Downloader 项目地址: https://gitcode.com/gh_mirrors/one/Onekey 还在为繁琐的Steam游戏清单获取流程而烦恼&#xff1f;Onekey Steam Depot…

作者头像 李华
网站建设 2026/4/15 12:50:07

Z-Image多机协作方案:云端团队共享工作区

Z-Image多机协作方案&#xff1a;云端团队共享工作区 引言&#xff1a;为什么团队需要共享AI工作区&#xff1f; 想象一下&#xff0c;你的设计团队有5位成员&#xff0c;每天都要使用Z-Image进行创意设计。如果每个人都自己搭建本地环境&#xff0c;不仅需要重复配置服务器、…

作者头像 李华
网站建设 2026/4/14 18:58:45

手势识别系统搭建:MediaPipe

手势识别系统搭建&#xff1a;MediaPipe 1. 引言&#xff1a;AI 手势识别与追踪 随着人机交互技术的不断演进&#xff0c;手势识别正逐步成为智能设备、虚拟现实、增强现实乃至工业控制中的核心感知能力。传统输入方式&#xff08;如键盘、鼠标&#xff09;在特定场景下存在局…

作者头像 李华