简介: 走马观花看过了数据相关的,接下来浏览一下高级功能,第一个高级功能 UGarbageCollection,顾名思义,UE 的垃圾回收
UGarbageCollection
UE 的垃圾回收系统,用来消除大部分的手动内存管理负担
UGarbageCollection 不是一个独立的类,是一套由引擎核心+UObject基础架构共同实现的分布式系统。要说翻阅其代码,它有一个垃圾回收驱动器用来驱动整套系统的正常运作,文件路径如下:
- 头文件(接口定义)Engine/Source/Runtime/CoreUObject/Public/UObject/GarbageCollection.h
- 算法实现 Engine/Source/Runtime/CoreUObject/Private/UObject/GarbageCollection.cpp
核心机制
-
自动根集管理
- 核心原则:标记所有被 UPROPERTY() 引用的对象
- 自动防护:保护玩家控制器、GameState 等核心对象
- 覆盖率:处理 95%+ 的对象存活需求
graph TD
UPROPERTY[UPROPERTY标记] --> 根集
GameState[GameState等核心对象] --> 根集
全局对象 --> 根集
根集 -. 保护 .-> 可达对象
不可达对象 --> GC回收
-
引用链追踪算法
- 关键优势:自动处理嵌套对象引用关系
- 性能优化:增量式扫描(每帧<5ms)
// 简化版追踪逻辑:
void MarkReachable(UObject* Obj) {
if(!Obj || Obj->IsMarked()) return;
Obj->Mark(); // 标记为存活
for(UProperty* Prop : Obj->GetProperties()) {
if(UObject** Ref = Prop->GetObjectRef()) {
MarkReachable(*Ref); // 递归追踪
}
}
}
-
对象声明周期钩子
- 覆盖 90% 的资源清理场景
- 安全释放:非托管资源(文件句柄/网络连接)
virtual void BeginDestroy() {
// 释放非托管资源(如D3D纹理)
Super::BeginDestroy();
}
virtual bool IsReadyForFinishDestroy() {
// 异步资源释放检查点
return TextureLoadCounter==0;
}
-
弱引用系统
- 解决痛点:80% 的悬挂指针风险
- 推荐场景:UI引用/事件监听/跨系统通信
TWeakObjectPtr<AActor> SafeRef; // 不阻止GC
if(SafeRef.IsValid()) {
// 对象仍然存在
}
⚙️ 20% 关键配置 (控制80%行为)
| 方法/机制 |
适用场景 |
使用频率 |
代码示例 |
UPROPERTY() |
所有对象引用保护 |
★★★★★ |
UPROPERTY() AActor* MyActor; |
MarkPendingKill() |
立即断开引用并标记待回收 |
★★★★★ |
Obj->MarkPendingKill(); |
TWeakObjectPtr<> |
安全跨帧引用 |
★★★★☆ |
TWeakObjectPtr<AActor> WeakRef; |
AddToRoot()/RemoveFromRoot()
|
创建永久对象 |
★★☆☆☆ |
Obj->AddToRoot(); |
FGCObject 抽象类 |
管理非UObject资源 |
★★★☆☆ |
继承并实现 AddReferencedObjects
|
ForceGarbageCollection() |
调试时强制GC |
★★☆☆☆ |
GEngine->ForceGarbageCollection(); |
GUObjectArray.DisregardForGC() |
临时豁免GC |
★☆☆☆☆ |
GUObjectArray.DisregardForGC(Obj); |
IsValidLowLevel() |
安全检测待回收对象 |
★★★★☆ |
if(IsValidLowLevel()) {...} |
配置使用原则:
- 99% 的情况使用
UPROPERTY() 足以保护对象
- 需要手动断开引用时优先使用
MarkPendingKill()
- 跨系统通信必须使用
TWeakObjectPtr<>
- 除非特殊需求,否则避免使用
AddToRoot()
阅读记录
ENUM_CLASS_FLAGS 宏的核心作用是将标准枚举类 (enum class) 扩展为支持位运算的位掩码类型,使其能够像整数一样进行按位操作,同时保持类型安全和编译时检查
| 特性 |
普通 enum
|
enum class |
ENUM_CLASS_FLAGS(enum class) |
| 类型安全 |
❌ 不安全 |
✅ 安全 |
✅ 安全 |
| 命名空间隔离 |
❌ 全局污染 |
✅ 隔离 |
✅ 隔离 |
| 位操作支持 |
✅ 需手动转换 |
❌ 不支持 |
✅ 自动支持 |
| 隐式整数转换 |
✅ 危险允许 |
❌ 严格限制 |
✅ 安全转换 |
| UE反射整合 |
❌ 有限支持 |
❌ 有限支持 |
✅ 完全支持 |
| 编译器类型检查 |
❌ 无 |
✅ 语法级检查 |
✅ 语法+语义检查 |
| 组合类型安全性 |
❌ 可与其他enum混合 |
✅ 强制类型匹配 |
✅ 运算符强制类型匹配 |
| IDE智能提示支持 |
❌ 不完整 |
✅ 完整 |
✅ 完整(含运算符提示) |
| 线程安全保证 |
❌ 依赖具体实现 |
❌ 依赖具体实现 |
✅ 原子操作实现 |
| 内存空间占用 |
编译器依赖(通常4B) |
显式控制(可1B) |
显式控制(同enum class) |
FName 全局字符串标识系统
- 引擎级的名称字典存储
-
轻量级哈希表:通过整数ID实现高速名称比较
-
大小写不敏感处理:
Weapon == WEAPON == weapon
| 特性 |
说明 |
| 内存占用 |
仅存储整数ID (固定4字节) |
| 比较速度 |
O(1) 常数时间 (直接整数比较) |
| 字符串内容 |
不可修改 (immutable) |
| 内存管理 |
全局共享池 (相同字符串单实例) |
FString 动态字符串
-
全功能字符串容器:类似C++的std::string
-
文本处理工具箱:包含100+字符串操作方法
-
动态内存管理:运行时可自由修改内容
| 特性 |
说明 |
| 内存占用 |
动态分配 (堆内存) + 小字符串优化(SSO) |
| 比较速度 |
O(n) 线性时间 (逐字符比较) |
| 字符串内容 |
可任意修改 (mutable) |
| 内存管理 |
完整UTF-16支持 |
FText
主要目标是为了处理需要本地化的文本
-
国际化支持引擎:自动处理多语言文本切换
-
格式安全系统:防注入攻击的文本格式化
-
文本内容抽象化:分离文本显示逻辑与具体内容
| 特性 |
说明 |
| 内存占用 |
智能指针 + 共享数据实例 (内存高效) |
| 比较机制 |
基于Key的比较 (非文本内容比较) |
| 内容特征 |
不可变 (immutable) + 线程安全 |
| 格式化保护 |
自动参数转义防止文本注入 |
| 自动刷新 |
语言切换时实时更新UI文本 |
FText vs FName vs FString
| 特性 |
FText |
FName |
FString |
| 设计目的 |
国际化显示 |
标识符管理 |
字符串操作 |
| 内存管理 |
✅ 共享实例 |
✅ 全局池 |
❌ 独立实例 |
| 线程安全 |
✅ 安全 |
✅ 安全 |
❌ 需同步 |
| 可变性 |
🔒 不可变 |
🔒 不可变 |
🔧 可变 |
| 比较方式 |
Key值比较 |
整数比较 |
文本比较 |
| 格式化安全 |
✅ 自动转义 |
❌ 不支持 |
❌ 不安全 |
| 多语言支持 |
✅ 完整 |
❌ 无 |
❌ 无 |
| 文本处理能力 |
⚠️ 受限 |
❌ 无 |
✅ 丰富 |
| 哈希计算 |
✅ 预计算 |
✅ 预计算 |
❌ 运行时计算 |
| 标识符类型 |
本地化Key |
唯一整数ID |
原始字符串 |
| 内存开销 |
中等 |
超低(4字节) |
中高 |
| 性能特征 |
初始化较慢/使用快 |
极快(整数比较) |
中等(字符遍历) |
| 典型用途 |
UI本地化文本 |
资源路径/标签 |
文件操作/拼接 |
| 引擎整合 |
Slate UI系统 |
UObject引用系统 |
工具类API |
| 字符串池 |
✅ 本地化池 |
✅ 全局名称池 |
❌ 无池化 |
| 序列化支持 |
✅ 含语言上下文 |
✅ 仅ID序列化 |
✅ 原始文本 |
| 内容来源 |
本地化资源文件 |
代码/配置定义 |
任意来源 |
| 输入风险 |
✅ 安全(自动转义) |
❌ 无保护 |
❌ 高危 |
| 字符集支持 |
✅ 全Unicode |
✅ 但转为大写 |
✅ 全Unicode |
| 运行时生成 |
❌ 受限 |
⚠️ 昂贵(更新池) |
✅ 完全支持 |