内存与 GC:看不见的游戏线程税
内存问题以两种形式伤害帧率:GC 停顿与分配开销。两者都隐蔽,都得专门治。
GC 停顿
UE 的 GC(垃圾回收)是分代的、主要在游戏线程上做标记:
- 偶发尖峰的常见来源之一就是 GC("大概每隔几秒卡一下"——第 2 章的形状表里有它);
- GC 的代价与对象数量与对象引用深度相关,不只是总内存。
| 手段 | 说明 |
|---|---|
| 减少不必要的 UObject | 用普通结构体 / POD 数组做数据容器 |
| 软引用(TSoftObjectPtr) | 不算硬引用,减轻标记成本 |
| 结构紧凑 | 避免深层嵌套的对象图 |
| 调 GC 间隔 | gc.TimeBetweenPurgingPendingKill(在项目设置或配置文件调) |
| 预分配容器 | TArray Reserve,避免运行时反复分配 |
分配开销
蓝图 / C++ 里每帧 new 数组、拼接字符串,都是在给分配器与 GC 增加负担:
- 热点路径避免分配:把容器提到成员变量、Reserve 好容量;
- 字符串拼接(FString +=)在 C++ 里也是分配大户,用 TEXT 宏与 FString::Printf 的习惯;
- 用
Memory Insights(Trace)定位分配热点:哪段代码在反复分配。
内存泄漏的排查
症状就是第 2 章的"随时间爬升"形状。工具链:
stat memory/stat objectcount看总量变化;- Memory Insights 的 Live Viewer 看对象增长;
- 怀疑对象池 / 缓存:打印池大小日志,确认只进不出。
一个常见但被忽视的泄漏:委托(EventDispatcher)订阅没解绑,对象被人持有引用而不释放。养成"订阅与解绑成对出现"的习惯。
