内存与 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 章的"随时间爬升"形状。工具链:

  1. stat memory / stat objectcount 看总量变化;
  2. Memory Insights 的 Live Viewer 看对象增长;
  3. 怀疑对象池 / 缓存:打印池大小日志,确认只进不出。

一个常见但被忽视的泄漏:委托(EventDispatcher)订阅没解绑,对象被人持有引用而不释放。养成"订阅与解绑成对出现"的习惯。