uobject野指针崩溃主因是uproperty()漏标,gc仅追踪标记指针;跨帧持有的uobject*必须加uproperty(含transient),局部裸指针禁用于容器或lambda,应改用tweakobjectptr并校验isvalid(),非uobject对象须用tsharedptr等管理。

UObject野指针崩溃,90%以上不是GC“误删”,而是你没让GC知道它该活下来。
UPROPERTY()漏标是野指针的第一大源头
GC只认被UPROPERTY()标记的指针——不是语法糖,是GC存活的唯一通行证。漏标一个UObject*成员变量,哪怕你在构造函数里new了、在Tick里反复用,几帧后它就可能被回收,而你的指针还静静挂着,变成野指针。
- 所有持有UObject指针的类成员,只要生命周期需跨帧,必须加
UPROPERTY()(哪怕只是UPROPERTY(Transient)) - 局部栈上声明的
UObject*不能直接存到容器或传给异步Lambda——它本身不被GC追踪,更别说它指向的对象 - 编辑器中拖入的引用如果没标
UPROPERTY(EditAnywhere)等,运行时根本不会进反射表,GC视而不见
TWeakObjectPtr不是万能解药,但它是Lambda里最安全的捕获方式
你在CreateLambda里捕获UObject*,比如处理FProperty反射遍历时传入的ParentAsset,一旦Lambda延迟执行(比如在资源加载回调里),原对象可能已被GC回收——此时解引用就是野指针。
- 改用
TWeakObjectPtr<udataasset></udataasset>捕获,而非裸指针:[WeakAsset = TWeakObjectPtr<udataasset>(ParentAsset)]</udataasset> - Lambda内部第一件事必须检查:
if (WeakAsset.IsValid()) { ... },无效就跳过,不碰Get() - 别用
TStrongObjectPtr替代——它会阻止GC回收,造成内存泄漏;TWeakObjectPtr只保引用可达性,不干预生命周期
非UObject对象不能混进UObject引用链
如果你在UObject派生类里存了一个原生C++对象指针(比如FMyStruct*或std::string*),哪怕加了UPROPERTY(),GC也会忽略它——因为GC只扫描UObject及其子类的指针字段。这类指针一旦所指内存被释放,立刻变野指针,且GC完全不管。
- 纯C++对象一律用
TSharedPtr/TUniquePtr管理,不要裸指针 - UObject里需要存非UObject数据时,优先用值语义(如
FString、TArray),避免堆分配+裸指针 - 若必须动态分配,确保析构函数里显式清理,并把指针置为
nullptr(防二次delete)
UE6.5之后,未标记UPROPERTY的裸指针存活率断崖下降
UE6.5的精确式GC大幅收紧了对“隐式引用”的容忍。以前靠全局变量、静态指针、甚至栈上临时引用“侥幸存活”的UObject,在新GC下大概率一帧就回收。这不是bug,是设计收敛。
- 别再依赖“我这儿还拿着指针,它应该还在”这种直觉——GC只看
UPROPERTY和根集(AddToRoot等) - 调试时打开
stat garbage,观察对象是否出现在“Marked”列表;用FindInBlueprints反查引用链,确认关键指针是否被正确标记 - 多线程回调中尤其危险:主线程刚标完
UPROPERTY,工作线程就已拿到裸指针并缓存——这个间隙就是野指针温床
真正难的从来不是写对一行UPROPERTY(),而是意识到:UObject的“存在感”不是由你new出来的那一刻决定的,而是由GC在下一帧标记阶段是否能顺着某条UPROPERTY链找到它来决定的。漏掉一个标记,等于亲手给指针埋下定时炸弹。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











