提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
Unreal Engine 的垃圾回收(Garbage Collection, GC)机制主要用于管理 UObject 及其派生类的生命周期,包括 UMG 控件(如 UWidget)。其核心是 基于根集(Root Set)的标记-清扫(Mark and Sweep)算法。以下是详细流程及代码示例:
提示:以下是本篇文章正文内容,下面案例可供参考
一、GC 的核心机制
1.1 根集(Root Set)
根集是 GC 的起点,包含所有不会被自动回收的对象,例如:
全局对象(GameInstance, World, PlayerController)
当前活跃的 Actor 和组件
通过 AddToRoot() 显式标记的对象
1.2 标记阶段(Marking)
GC 从根集出发,递归遍历所有被引用的 UObject,标记为“活跃”。
1.3 清扫阶段(Sweeping)
未被标记的对象会被销毁,内存被回收。
二、控件生命周期管理
UMG 控件(如 UUserWidget)继承自 UObject,生命周期由 GC 管理,其存活与否取决于是否被根集引用。
关键流程:
2.1 控件创建
件通常通过 CreateWidget 创建,此时未被根集引用,需手动添加到父容器或视口:
UUserWidget* MyWidget = CreateWidget<UUserWidget>(GetWorld(), WidgetClass);
2.2 添加到视口(成为根集)
当控件被添加到视口时,会被 Viewport 引用,从而成为根集的一部分:
MyWidget->AddToViewport(); // 控件被根集引用,GC 不会回收
2.3 移除视口(断开根集)
调用 RemoveFromParent() 后,控件不再被视口引用。若没有其他 UPROPERTY 或根引用,将在下次 GC 时被回收:
MyWidget->RemoveFromParent(); // 断开与根集的连接
2.4 显式销毁
可手动调用 ConditionalBeginDestroy() 触发销毁逻辑:
MyWidget->ConditionalBeginDestroy(); // 立即标记为待销毁
三、代码示例与流程分析
3.1 控件创建与添加
// 创建控件(未被根集引用)
UUserWidget* MyWidget = CreateWidget<UUserWidget>(GetWorld(), WidgetClass);
// 添加到视口(成为根集一部分)
MyWidget->AddToViewport();
此时 MyWidget 被 Viewport 引用,GC 不会回收。
3.2 控件移除与 GC 回收
// 从视口移除(断开根集引用)
MyWidget->RemoveFromParent();
// 触发 GC(通常在下一帧自动执行)
GetWorld()->ForceGarbageCollection(true);
移除后若没有其他引用,MyWidget 会被 GC 回收。
3.3防止误回收:使用 UPROPERTY
通过 UPROPERTY 声明强引用:
UPROPERTY()
UUserWidget* MyWidget; // 强引用,阻止 GC 回收
void CreateWidget()
{
MyWidget = CreateWidget<UUserWidget>(...);
MyWidget->AddToViewport();
}
当 MyWidget 被移除视口时,由于 UPROPERTY 存在,仍不会被 GC 回收,需手动置空:
MyWidget->RemoveFromParent();
MyWidget = nullptr; // 允许 GC 回收
四、GC触发时机
自动触发:每 60 秒或内存压力较大时。
手动触发:通过 GetWorld()->ForceGarbageCollection(true);。
五、调试与注意事项
5.1 检查引用链
使用 GUObjectArray 或控制台命令 OBJ REFS 查看对象引用关系。
5.2 弱引用
使用 TWeakObjectPtr 避免强引用导致内存泄漏:
TWeakObjectPtr<UUserWidget> WeakWidgetPtr = MyWidget;
5.3 异步加载
异步加载的控件需确保在销毁前取消加载任务,避免野指针。
总结
Unreal 的 GC 通过根集引用链管理控件生命周期,关键点在于:
控件添加到视口时被根集保护。
移除视口后若无其他引用,GC 自动回收。
使用 UPROPERTY 或 TWeakObjectPtr 精确控制引用关系。