轉自:https://blog.csdn.net/noahzuo/article/details/73565259
UObject和FUObjectItem
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)。
