• UE4的内存模型


    转自:https://blog.csdn.net/noahzuo/article/details/73565259

    UObjectFUObjectItem

    UE4运行的基本单位是UObjet,然而UObject针对于内存相关的东西都储存在结构体FUObjectItem中。有个全局变量GUObjectArray,可以通过以下代码来遍历所有的Objects:

    for (FRawObjectIterator It(false); It; ++It)
    {
        FUObjectItem* ObjectItem = *It;
        UObject* obj = ObjectItem->Object;
    }

    可以通过如下代码来获得一个UObject对应的ObjectItem

    FUObjectItem* ObjectItem = GUObjectArray.ObjectToObjectItem(Object);

    GUObjectArray中有几个函数可以通过索引和ObjectItem、Object的相互获取,对应的函数为IndexToObject、ObjectToIndex、IndexToObjectUnsafeForGC等。

    GUObjectArray中所有的UObjectArray的分布是有顺序的——那些不会被GC的UObject,例如各StaticClass、核心Object,GamePlayInstance等会放在前面;而那些有可能被GC的UObject,则放在后面。此外,GUObjectArray中有个变量ObjLastNonGCIndex,用于分隔这两类UObject。

    /**
     * Returns true if this object is "disregard for GC"...same results as the legacy RF_DisregardForGC flag
     *
     * @param Object object to get for disregard for GC
     * @return true if this object si disregard for GC
     */
    FORCEINLINE bool IsDisregardForGC(const class UObjectBase* Object)
    {
      return Object->InternalIndex <= ObjLastNonGCIndex;
    }

    FUObjectCluster相关
    Cluster指得是一组UObject为一簇,这群UObject同生共死,每个Cluster有一个根root的UObject。

    每一个FUObjectItem里面有一个变量ClusterRootIndex,这个储存的是当前Object所在的Cluster的root object所在GUObjectArray的索引。

        // UObjectArray.h
        // UObject Owner Cluster Index
        int32 ClusterRootIndex; 

    引擎中有一个全局变量FUObjectClusterContainer GUObjectClusters,用于管理内存中所有的Clusters。

    // Get the number of all clusters that have been allocated. 
    GUObjectClusters.GetNumAllocatedClusters()

    但很奇怪的,在我新建的若干个测试关卡/项目中,这个值一直为0……

    • 一个关卡ULevel不可以成为一个Cluster的root,原因是在这个时候(postload之后)仍然有很多被Level引用的assets并未构建它们自己的Cluster。
    bool ULevel::CanBeClusterRoot() const
    {
    // We don't want to create the cluster for levels in the same place as other clusters (after PostLoad)
    // because at this point some of the assets referenced by levels may still haven't created clusters themselves.
    return false;
    }

    虽然ULevel本身不可以作为Cluster Root,而相反的是它会创建一个特殊的actor container,用来储存原本应该位于Cluster的actors。这是由于只有某些特殊的actor种类才能用于Cluster,所以剩下的那些不能被cluster的actors需要通过actor container来进行引用。

    ULevel中使用ClusterActors数组用于储存那些用于Cluster的Actors;用ActorsForGC储存那些剩下的Actors。主食中提到不希望Level直接去引用那些本就是Cluster的Actors,注释中提到这会导致变慢(针对于cluster的Reference很慢,可能是如果引用了cluster里面的actor,那么会导致整个cluster也会添加对应的引用关系,从而导致引用的层级变多吧……)

        TArray<AActor*> ClusterActors;
        for (int32 ActorIndex = Actors.Num() - 1; ActorIndex >= 0; --ActorIndex)
        {
            AActor* Actor = Actors[ActorIndex];
            if (Actor && Actor->CanBeInCluster())
            {
            ClusterActors.Add(Actor);
            }
            else
            {
            ActorsForGC.Add(Actor);
            }
        }

    引用相关

    • 如何获得一个UObject所引用的其他UObject
    TArray<UObject*> CollectedReferences;
    FReferenceFinder ObjectReferenceCollector(CollectedReferences);
    ObjectReferenceCollector.FindReferences(Object);

    一度看了看Reference是怎么跑起来的,后来发现大部分的从UClass派生出来的类会制定对应的ClassAddReferenceObjects方法,这个方法用于制定该类会和哪些东西产生对应的引用……

    不受内存管理的内存

      • malloc & free
      • new & delete
        new与malloc的区别在于,new在分配内存完成之后会调用构造函数。

    内存管理的内存

    • 对于不是继承自UObject的Native C++类,使用TSharedPtr、TAutoPtr、TWeakPtr、TSharedRef、TScopedPointer管理
    • 对于继承自UObject的子类
      创建: UObject::NewObject<> 或是 UObject::ConstructObject<>,其中ConstructObject可以做更复杂的参数配置
      销毁:当计数为0时,自动释放;调用UObject::ConditionalBeginDestroy()手动释放。若要强制调用垃圾回收,则调用UWorld::ForceGarbageCollection(true)。
    • 对于继承自AActor的子类
      创建: UWorld::SpawnActor<>
      销毁: AActor::Destroy()
    • TArray<>数组需要用UPROPERTY()修饰,否则会导致内存管理错误
    • 继承自UActorComponent的组件,使用AActor::CreateDefaultSubobject<>,同样组件的指针变量也需要用UPROPERTY()修饰。

    1.什么样的对象可以被unreal engine管理和回收:
    unreal engine定义的类中,只有UObject或者是UObject的派生类创建的对象,才能被unreal engine管理,用完后才能被unreal engine回收。其它类比如:UStructs,不具有这个特性。

    2.UObject或者是UObject的派生类的实例,创建和销毁的方式:
    1).AActor类或者AActor派生类,虽然也是UObject的派生类,但创建和销毁方式不同于一般UObject的派生类:

    AActor创建方式:

    UWorld::SpawnActor() //此方法创建Actor实例后,UWorld会持有Actor实例的引用

    AActor销毁的方式:

    AActor::Destroy() //此方法会将Actor实例从关卡中删除,并将Actor实例标记为“待杀死”,然后会在下一次GC时删除


    2).除AActor类或者AActor派生类以外的其它UObject或者是UObject的派生类:

    UObject可通过如下4种方式创建:

    NewObject<class>()
    NewNamedObject<class>()
    ConstructObject<class>()
    new

    UObject销毁方式:

    UObject::MarkPendingKill() //此方法执行后,所有指向此实例的指针将设置为NULL,并在下一次GC时删除

    3.垃圾收集器如何管理UObject的实例
    1).AActor类或者AActor派生类的实例被创建后,会自动存放在垃圾收集器的对象根集合中,不会被自动回收。

    2).除AActor类或者AActor派生类以外的其它UObject或者是UObject的派生类的实例被创建后,会自动被回收,如果想不被GC回收,主要有如下几种方式:

    2.1).创建的实例作为UObject的派生类的成员变量,并且被标记为UPROPERTY()

    2.2).创建的实例存放在TArray中,TArray作为UObject的派生类的成员变量,并且被标记为UPROPERTY()

    2.3).创建的实例存放在智能指针中。(此说法待验证)

    2.4).通过UObject::AddToRoot(),设置RF_RootSet标志(可参考:UObject Instance Creation)。

  • 相关阅读:
    Jenkins参数化构建
    python笔记
    jenkins定时任务
    技巧:Vimdiff 使用
    clover如何使用UEFI引导和EFI驱动选择
    Broadcast BCM94322 用ubuntu修改ID
    MAC实现睡眠和休眠唤醒
    MAC的睡眠模式介绍
    linux 用dd命令读写引导区文件
    MAC下打开FTP服务
  • 原文地址:https://www.cnblogs.com/sevenyuan/p/11800306.html
Copyright © 2020-2023  润新知