【UE4的垃圾回收】

UE4的垃圾回收

  • [1 UObjects及子类](#1 UObjects及子类)
    • [1.1 UObjects类包含UObjects成员(UPROPERTY)](#1.1 UObjects类包含UObjects成员(UPROPERTY))
    • [1.2 UObjects类包含非UObjects成员](#1.2 UObjects类包含非UObjects成员)
  • [2 非UObject及子类](#2 非UObject及子类)
    • [2.1 非UObjects类包含UObjects成员1](#2.1 非UObjects类包含UObjects成员1)
    • [2.2 非UObjects类包含UObjects成员2](#2.2 非UObjects类包含UObjects成员2)
    • [2.3 非UOjbects类包含非UObjects成员](#2.3 非UOjbects类包含非UObjects成员)
  • [3 UStructs](#3 UStructs)
  • [4 容器类(TArray、TMap、TSet)](#4 容器类(TArray、TMap、TSet))

1 UObjects及子类

虚幻4引擎使用反射系统(机制)去实现垃圾回收。关于垃圾回收,你不用进行手动的去销毁你的UObjects类对象,你只需要保持对他们的引用。你的类需要继承UObject 类以支持垃圾回收。下面有个简单的例子。

cpp 复制代码
UCLASS()
class MyGCType : public UObject
{
    GENERATED_BODY()
};

在垃圾回收器里,这里有个概念被称为 root set (根集)。根集是一个基本的对象列表,回收器不会回收根集的对象。一个对象的引用路径如果在根集里,那么它将不会被回收。如果一个对象不存在这种引用路径,称之为"unreachable"(无法访问),并且在下次垃圾回收器运行时被回收(销毁)。引擎将在一定时间间隔运行垃圾回收。

1.1 UObjects类包含UObjects成员(UPROPERTY)

本节以AActors(继承自UObjects)的一个继承类AMyActor为例说明。

Actor通常不被垃圾回收。当你在关卡中生成Actor后,如果需要在关卡卸载前销毁Actor,你需要手动的调用Destroy() 方法去销毁,但它们不会被立刻的销毁,而是在下一个垃圾回收时期被销毁。

下面是一个常见的情况,你的Actor有UObject 类型的属性。

cpp 复制代码
UCLASS()
class AMyActor : public AActor
{
    GENERATED_BODY()

public:
    UPROPERTY()
    MyGCType* SafeObject;

    MyGCType* DoomedObject;

    AMyActor(const FObjectInitializer& ObjectInitializer)
        : Super(ObjectInitializer)
    {
        SafeObject = NewObject<MyGCType>();
        DoomedObject = NewObject<MyGCType>();
    }
};

void SpawnMyActor(UWorld* World, FVector Location, FRotator Rotation)
{
    World->SpawnActor<AMyActor>(Location, Rotation);
}

当我们调用上面的函数时,我们在世界中生成一个Actor。Actor的构造函数创建2个对象。一个被标记了UPROPERTY,另一个使用普通的指针。由于Actor本来就是根集的一部分,SafeObject 将不会被垃圾回收,因为它在根集中可以被访问。然而,DoomedObject将不会那么幸运,我们没有对它标记UPROPERTY,所以回收器将不知道它的引用,所以事实上它会被销毁。

当一个UObject 被垃圾回收,所有的UPROPERTY 类型的引用都将变成空指针。你最好在使用时检查一下是否存在空指针。

cpp 复制代码
if (MyActor->SafeObject != nullptr)
{
    // Use SafeObject
}

正如我前面提到的,这非常的重要,Actor如果已经执行了Destroy() 方法,它将不会被移除,直到下次垃圾回收。你可以使用IsPendingKill() 方法去检查,这个UObject是否在被等待销毁。如果方法返回Ture,意味着这个UObject 已经无用了。
总结:1)UObjects对象本身已经加入回收机制;2)成员为UObjects对象时,需要UPROPERTY()以加入回收机制,否则不能加入回收机制。

1.2 UObjects类包含非UObjects成员

总结:1)UObjects对象本身已经加入回收机制;2)成员不为UObjects对象时,比如:基本数据类型(int、float)指针、结构体指针等,自行new/delete。

2 非UObject及子类

2.1 非UObjects类包含UObjects成员1

通常,非UObjects对象它能够添加一个对象引用而避免被垃圾回收。为了达到这种效果,你的类必须继承FGCObject ,并且重写AddReferencedObjects 。

cpp 复制代码
class FMyNormalClass : public FGCObject
{
public:
    UObject* SafeObject;

    FMyNormalClass(UObject* Object)
        : SafeObject(Object)
    {
    }

    void AddReferencedObjects(FReferenceCollector& Collector) override
    {
        Collector.AddReferencedObject(SafeObject);
    }
};

我们使用FReferenceCollector ,为我们所需要的UObject 对象,手动添加一个硬引用,使其不能被垃圾回收。当这个对象(FMyNormalClass)被销毁并且析构函数执行,该对象将会自动清除它所添加的引用。
总结:1)非UObjects对象借助UObjects成员且继承FGCObject,同时重写AddReferencedObjects,使得非UObjects对象与UObjects成员都加入回收机制。

2.2 非UObjects类包含UObjects成员2

如果在非UObjects类中有一个UObject* A变量,那么在创建完该变量之后,最好在非UObjects类的构造函数中,使用AddToRoot,这样该变量就不会被UE4自动GC。

cpp 复制代码
UObject* A = NewObject();
A->AddToRoot();

然后在非UObjects类的析构函数中,使用RemoveFromRoot即可自动让UE4GC,防止内存泄漏。

cpp 复制代码
A->RemoveFromRoot();

总结:1)非UObjects对象包含的UObjects成员,通过AddToRoot和RemoveFromRoot,使得UObjects成员都加入回收机制;2)非UObjects对象,自行new/delete。

2.3 非UOjbects类包含非UObjects成员

总结:自行new/delete。

3 UStructs

UE4中提到:
"UStructs,正如前面提到,可以理解为轻量级的UObject。比如说,UStructs 不会被垃圾回收。如果你必须要用UStructs 类型的动态实例,你可能需要使用智能指针去代替,我们后面会提到。"

自己认为:
同"2 非UObject及子类"的说明。

4 容器类(TArray、TMap、TSet)

容器类一般作为类成员存在,以下按照其存储内容介绍。

4.1.当容器类存储了任意 UObjects指针

当容器类存储了任意 UObjects指针,也可被视为将UObjects对象加入垃圾回收机制,但是需要在以下两种情况中:1)当容器类作为UObjects类的成员时,需要加UPROPERTY;2)当容器类作为非UObjects类的成员时,需要非UObjects类继承FGCObject,并重写FGCObject::AddReferencedObjects()函数,与"2.1 非UObjects类包含UObjects成员1"不同的是,需要在FGCObject::AddReferencedObjects()内调用FReferenceCollector::AddReferencedObjects()函数(有多个对容器类的重载版本)。

4.2.当容器类存储了UObjects类外的指针

请自行new/delete。

4.3.UE4帮助文档摘抄

  1. 摘抄1
    UPROPERTY 或UE4容器类(例如TArray)中存储的任意 UObject 指针都被视为垃圾回收的"引用"。首先让我们从简单示例入手。
  2. 摘抄2
    TArray 添加了对其元素进行垃圾回收的好处。这样会假设 TArray 存储了 UObject 派生的指针。
cpp 复制代码
UCLASS()
class UMyClass : UObject
{
    GENERATED_BODY();

    // ...

    UPROPERTY()
    AActor* GarbageCollectedActor;

    UPROPERTY()
    TArray<AActor*> GarbageCollectedArray;

    TArray<AActor*> AnotherGarbageCollectedArray; //从这看,不加UPROPERTY也行
};
相关推荐
致命的邂逅15 天前
UE4游戏查找本地角色数据的方法-SDK
游戏·ue4
北冥没有鱼啊17 天前
UE 材质几个输出向量节点
ue5·游戏引擎·ue4·虚幻·材质
北冥没有鱼啊1 个月前
UE 材质 条纹循环发光
游戏·ue5·游戏引擎·ue4·材质
北冥没有鱼啊1 个月前
UE 滚动提示条材质制作
游戏·ue5·游戏引擎·ue4·虚幻·材质
vivo互联网技术2 个月前
Full GC 频率优化实战
后端·系统优化·java8·垃圾回收
北冥没有鱼啊2 个月前
UE 使用事件分发器设计程序
游戏·ue5·ue4·游戏开发·虚幻
codkingo2 个月前
Go - 内存逃逸
golang·垃圾回收
GR902 个月前
UE4 踩坑记录
ue4
程序猿熊跃晖2 个月前
解决 Unreal Engine 5.2 中服务器目标构建问题:从源码编译到项目配置优化
ue4
Ⅰㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ2 个月前
开篇 - Unlua+VsCode的智能提示、调试
vscode·ue4·智能提示·unlua