UE4 GC原理
UObject概况
在开始之前,需要先介绍一下UE4是怎样管理UObject的,UObject其实并不是最上层的基类,UE4可能是觉得代码都写在UObject里太多了,看起来比较麻烦,所以就让UObject还继承着别的基类,这样可以把不同代码写到不同的基类里。其中最上层的基类是UObjectBase,他在创建的时候会把自己交给UE4的两个全局容器来管理,在销毁的时候把自己从管理自己的容器中移除
- GUObjectArray : 类型是 FUObjectArray
- FUObjectHashTables : FUObjectHashTables::Get()
UObject 继承关系
class UObject : public UObjectBaseUtility
{
}
class UObjectBaseUtility : public UObjectBase
{
}
要防止对象被GC,有4种方式:
- 作为成员变量并标记为UPROPERTY();
- 创建对象后 AddToRoot() ;(退出游戏时需要RemoveFromRoot())
- FStreamableManager Load资源时,bManageActiveHandle 设置为true;
- FGCObjectScopeGuard 在指定代码区域内保持对象;
UObject 创建时,会将自己添加到两个全局容器中:
UObjectBase::UObjectBase(UClass* InClass, EObjectFlags InFlags,
EInternalObjectFlags InInternalFlags, UObject *InOuter, FName InName)
: ObjectFlags (InFlags)
, InternalIndex (INDEX_NONE)
, ClassPrivate (InClass)
, OuterPrivate (InOuter)
{
check(ClassPrivate);
// Add to global table.
AddObject(InName, InInternalFlags);
}
void UObjectBase::AddObject(FName InName, EInternalObjectFlags InSetInternalFlags)
{
// 把自己添加到 UObjectArray 里,并且为 Object 分配 InternalIndex
AllocateUObjectIndexForCurrentThread(this);
check(InName != NAME_None && InternalIndex >= 0);
// 把自己添加到 FUObjectHashTables 中
HashObject(this);
}
FUObjectArray 介绍
FUObjectArray 基本代码结构
class FUObjectArray
{
private:
/** First index into objects array taken into account for GC.*/
int32 ObjFirstGCIndex;
/** Index pointing to last object created in range disregarded for GC.*/
int32 ObjLastNonGCIndex;
typedef FChunkedFixedUObjectArray TUObjectArray;
TUObjectArray ObjObjects;
};
class FChunkedFixedUObjectArray
{
enum
{
NumElementsPerChunk = 64 * 1024,
};
// Master table to chunks of pointers
FUObjectItem** Objects;
// If requested, a contiguous memory where all objects are allocated
FUObjectItem* PreAllocatedObjects;
// Maximum number of elements : 64 * 1024
int32 MaxElements;
// Number of elements we currently have
int32 NumElements;
// Maximum number of chunks
// Max UObject / NumElementsPerChunk
// 25165824 = 24 * 1024 * 1024
// 384
int32 MaxChunks;
/** Number of chunks we currently have **/
int32 NumChunks;
}
FUObjectArray 会根据当前已有的 UObject 个数创建 Chunk 数组,每个 Chunk 可以容纳 64 * 1024 个对象。UObject->InternalIndex 其实就是表示该对象在 FUObjectArray 上的位置
Index = Object->InternalIndex;
// NumElementsPerChunk 是个常量 64 * 1024
const int32 ChunkIndex = Index / NumElementsPerChunk;
const int32 WithinChunkIndex = Index % NumElementsPerChunk;
void AllocateUObjectIndexForCurrentThread(UObjectBase* Object)
{
GUObjectArray.AllocateUObjectIndex(Object);
}
void FUObjectArray::AllocateUObjectIndex(UObjectBase* Object,
bool bMergingThreads /*= false*/)
{
// 根据当前已经有的 UObject个数,计算需要的 Chunk 个数
// 如果 Chuck满了,则分配新的 Chuck
// 每个 Chunk 可以放 64 * 1024 个UObject
// 空余的Index :对象销毁的时候,会把自身的 Index
// 放到 ObjAvailableList 中
int32* AvailableIndex = ObjAvailableList.Pop();
if (AvailableIndex)
{
Index = (int32)(uintptr_t)AvailableIndex;
check(ObjObjects[Index].Object==nullptr);
}
else
{
Index = ObjObjects.AddSingle();
}
// 给 FUObjectItem Object 指针 指向 新增的 UObject
if (FPlatformAtomics::InterlockedCompareExchangePointer(
(void**)&ObjObjects[Index].Object, Object, NULL) != NULL)
{
UE_LOG(LogUObjectArray, Fatal, TEXT("Unexpected
concurency while adding new object"));
}
Object->InternalIndex = Index;
}
// Object 销毁
UObjectBase::~UObjectBase()
{
// If not initialized, skip out.
if( UObjectInitialized() && ClassPrivate && !GIsCriticalError )
{
// Validate it.
check(IsValidLowLevel());
check(GetFName() == NAME_None);
GUObjectArray.FreeUObjectIndex(this);
}
}
void FUObjectArray::FreeUObjectIndex(UObjectBase* Object)
{
int32 Index = Object->InternalIndex;
// At this point no two objects exist with the same index
// so no need to lock here
if (FPlatformAtomics::InterlockedCompareExchangePointer(
(void**)&ObjObjects[Index].Object, NULL, Object) == NULL)
{
UE_LOG(LogUObjectArray, Fatal, TEXT("Unexpected concurency
while adding new object"));
}
if (Index > ObjLastNonGCIndex && !GExitPurge)
{
ObjAvailableList.Push((int32*)(uintptr_t)Index);
}
}
FUObjectItem 结构
struct FUObjectItem
{
// Pointer to the allocated object
class UObjectBase* Object;
// Internal flags
int32 Flags;
// UObject Owner Cluster Index
int32 ClusterRootIndex;
// Weak Object Pointer Serial number associated with the object
int32 SerialNumber;
}
// Flags : 标记位
enum class EInternalObjectFlags : int32
{
None = 0,
//~ All the other bits are reserved, DO NOT ADD NEW FLAGS HERE!
///< External reference to object in cluster exists
ReachableInCluster = 1 << 23,
///< Root of a cluster
ClusterRoot = 1 << 24,
///< Native (UClass only).
Native = 1 << 25,
///< Object exists only on a different thread than the game thread.
Async = 1 << 26,
///< Object is being asynchronously loaded.
AsyncLoading = 1 << 27,
// mark
///< Object is not reachable on the object graph.
Unreachable = 1 << 28,
///< Objects that are pending destruction
// (invalid for gameplay but valid objects)
PendingKill = 1 << 29,
// mark
///< Object will not be garbage collected, even if unreferenced.
RootSet = 1 << 30,
//~ UnusedFlag = 1 << 31,
GarbageCollectionKeepFlags = Native | Async | AsyncLoading,
//~ Make sure this is up to date!
AllFlags = ReachableInCluster | ClusterRoot | Native | Async
| AsyncLoading | Unreachable | PendingKill | RootSet
};
UObject 析构时,会把自己从全局数组中删除,重新创建 UObject 时,原来删除的空位会被重新分配给新的对象,原来的下标会指向新的对象,为了防止业务保存原来的下标取错对象,FUObjectItem 中有一个唯一 ID: SerialNumber。 SerialNumber 是一个自增不重复的的ID,可以用来唯一标识一个 UObject
int32 FUObjectArray::AllocateSerialNumber(int32 Index)
{
FUObjectItem* ObjectItem = IndexToObject(Index);
checkSlow(ObjectItem);
volatile int32 *SerialNumberPtr = &ObjectItem->SerialNumber;
int32 SerialNumber = *SerialNumberPtr;
if (!SerialNumber)
{
//////////////////////////////////////////////
SerialNumber = MasterSerialNumber.Increment();
//////////////////////////////////////////////
UE_CLOG(SerialNumber <= START_SERIAL_NUMBER, LogUObjectArray, Fatal,
TEXT("UObject serial numbers overflowed (trying to
allocate serial number %d)."), SerialNumber);
int32 ValueWas = FPlatformAtomics::InterlockedCompareExchange(
(int32*)SerialNumberPtr, SerialNumber, 0);
if (ValueWas != 0)
{
// someone else go it first, use their value
SerialNumber = ValueWas;
}
}
checkSlow(SerialNumber > START_SERIAL_NUMBER);
return SerialNumber;
}
容器 FUObjectArray 初始化
在游戏启动时,会初始化全局容器 FUObjectArray
void UObjectBaseInit()
{
int32 MaxUObjects = 2 * 1024 * 1024; // Default to ~2M UObjects
if (FPlatformProperties::RequiresCookedData())
{
// Maximum number of UObjects in cooked game
GConfig->GetInt(TEXT("/Script/Engine.GarbageCollectionSettings"),
TEXT("gc.MaxObjectsInGame"), MaxUObjects, GEngineIni);
}
else
{
#if IS_PROGRAM
// Maximum number of UObjects for programs can be low
MaxUObjects = 100000; // Default to 100K for programs
GConfig->GetInt(TEXT("/Script/Engine.GarbageCollectionSettings"),
TEXT("gc.MaxObjectsInProgram"), MaxUObjects, GEngineIni);
#else
// Maximum number of UObjects in the editor
GConfig->GetInt(TEXT("/Script/Engine.GarbageCollectionSettings"),
TEXT("gc.MaxObjectsInEditor"), MaxUObjects, GEngineIni);
#endif
}
GUObjectArray.AllocateObjectPool(MaxUObjects, MaxObjectsNotConsideredByGC,
bPreAllocateUObjectArray);
}
void FUObjectArray::AllocateObjectPool(int32 InMaxUObjects,
int32 InMaxObjectsNotConsideredByGC, bool bPreAllocateObjectArray)
{
ObjObjects.PreAllocate(InMaxUObjects, bPreAllocateObjectArray);
}
void FChunkedFixedUObjectArray::PreAllocate(int32 InMaxElements,
bool bPreAllocateChunks)
{
MaxChunks = InMaxElements / NumElementsPerChunk + 1;
MaxElements = MaxChunks * NumElementsPerChunk;
Objects = new FUObjectItem*[MaxChunks];
}
MaxObjects 配置如下:
Android 配置:
标记清除GC过程
GC 标记流程
入口为UObjectGlobals.h中定义的CollectGarbage()函数,如下:
- 获取GC锁
- 执行CollectGarbageInternal
- 释放GC锁
void CollectGarbage(EObjectFlags KeepFlags, bool bPerformFullPurge)
{
// No other thread may be performing UObject operations while we're running
AcquireGCLock();
// Perform actual garbage collection
CollectGarbageInternal(KeepFlags, bPerformFullPurge);
// Other threads are free to use UObjects
ReleaseGCLock();
}
可达性分析:
void CollectGarbageInternal(EObjectFlags KeepFlags,
bool bPerformFullPurge)
{
// Perform reachability analysis.
{
const double StartTime = FPlatformTime::Seconds();
FRealtimeGC TagUsedRealtimeGC;
TagUsedRealtimeGC.PerformReachabilityAnalysis(KeepFlags,
bForceSingleThreadedGC, bWithClusters);
UE_LOG(LogGarbage, Log, TEXT("%f ms for GC"),
(FPlatformTime::Seconds() - StartTime) * 1000);
}
}
void PerformReachabilityAnalysis(EObjectFlags KeepFlags,
bool bForceSingleThreaded, bool bWithClusters)
{
FGCArrayStruct* ArrayStruct = FGCArrayPool::Get().GetArrayStructFromPool();
TArray<UObject*>& ObjectsToSerialize = ArrayStruct->ObjectsToSerialize;
const double StartTime = FPlatformTime::Seconds();
(this->*MarkObjectsFunctions[GetGCFunctionIndex(!bForceSingleThreaded,
bWithClusters)])(ObjectsToSerialize, KeepFlags);
}
static FORCEINLINE int32 GetGCFunctionIndex(bool bParallel, bool bWithClusters)
{
return (int32(bParallel) | (int32(bWithClusters) << 1));
}
class FRealtimeGC : public FGarbageCollectionTracer
{
MarkObjectsFn MarkObjectsFunctions[4];
ReachabilityAnalysisFn ReachabilityAnalysisFunctions[4];
FRealtimeGC()
{
MarkObjectsFunctions[GetGCFunctionIndex(false, false)] =
&FRealtimeGC::MarkObjectsAsUnreachable<false, false>;
MarkObjectsFunctions[GetGCFunctionIndex(true, false)] =
&FRealtimeGC::MarkObjectsAsUnreachable<true, false>;
MarkObjectsFunctions[GetGCFunctionIndex(false, true)] =
&FRealtimeGC::MarkObjectsAsUnreachable<false, true>;
MarkObjectsFunctions[GetGCFunctionIndex(true, true)] =
&FRealtimeGC::MarkObjectsAsUnreachable<true, true>;
ReachabilityAnalysisFunctions[GetGCFunctionIndex(false, false)] =
&FRealtimeGC::PerformReachabilityAnalysisOnObjectsInternal
<EFastReferenceCollectorOptions::None |
EFastReferenceCollectorOptions::None>;
ReachabilityAnalysisFunctions[GetGCFunctionIndex(true, false)] =
&FRealtimeGC::PerformReachabilityAnalysisOnObjectsInternal
<EFastReferenceCollectorOptions::Parallel |
EFastReferenceCollectorOptions::None>;
ReachabilityAnalysisFunctions[GetGCFunctionIndex(false, true)] =
&FRealtimeGC::PerformReachabilityAnalysisOnObjectsInternal
<EFastReferenceCollectorOptions::None |
EFastReferenceCollectorOptions::WithClusters>;
ReachabilityAnalysisFunctions[GetGCFunctionIndex(true, true)] =
&FRealtimeGC::PerformReachabilityAnalysisOnObjectsInternal
<EFastReferenceCollectorOptions::Parallel |
EFastReferenceCollectorOptions::WithClusters>;
}
}
标记函数实现如下:
UE使用了簇(Cluster)来提高效率,Cluster 是一组 UObject ,在 GC 流程中被视为一个单一的单位,能加速 GC。Cluster 后面再仔细研究下。
/// 标记函数
template <bool bParallel, bool bWithClusters>
void MarkObjectsAsUnreachable(TArray<UObject*>& ObjectsToSerialize,
const EObjectFlags KeepFlags)
{
const EInternalObjectFlags FastKeepFlags =
EInternalObjectFlags::GarbageCollectionKeepFlags;
// 从全局数组 GUObjectArray 中获取需要 GC UObject 个数
const int32 MaxNumberOfObjects = GUObjectArray.GetObjectArrayNum()
- GUObjectArray.GetFirstGCIndex();
// 计算每个 Work 线程需要处理的 UObject 个数
const int32 NumThreads = FMath::Max(1,
FTaskGraphInterface::Get().GetNumWorkerThreads());
const int32 NumberOfObjectsPerThread = (MaxNumberOfObjects / NumThreads) + 1;
for (int32 ThreadIndex = 0; ThreadIndex < NumThreads; ++ThreadIndex)
{
ObjectsToSerializeArrays[ThreadIndex] =
FGCArrayPool::Get().GetArrayStructFromPool();
}
// worker 线程分段对整个 GUObjectArray 处理
ParallelFor(NumThreads, [ObjectsToSerializeArrays, &ClustersToDissolveList,
&KeepClusterRefsList, FastKeepFlags, KeepFlags, NumberOfObjectsPerThread,
NumThreads, MaxNumberOfObjects](int32 ThreadIndex)
{
int32 FirstObjectIndex = ThreadIndex * NumberOfObjectsPerThread
+ GUObjectArray.GetFirstGCIndex();
int32 NumObjects = (ThreadIndex < (NumThreads - 1)) ?
NumberOfObjectsPerThread : (MaxNumberOfObjects -
(NumThreads - 1) * NumberOfObjectsPerThread);
int32 LastObjectIndex = FMath::Min(GUObjectArray.GetObjectArrayNum() - 1,
FirstObjectIndex + NumObjects - 1);
int32 ObjectCountDuringMarkPhase = 0;
TArray<UObject*>& LocalObjectsToSerialize =
ObjectsToSerializeArrays[ThreadIndex]->ObjectsToSerialize;
for (int32 ObjectIndex = FirstObjectIndex; ObjectIndex <= LastObjectIndex;
++ObjectIndex)
{
FUObjectItem* ObjectItem =
&GUObjectArray.GetObjectItemArrayUnsafe()[ObjectIndex];
if (ObjectItem->Object)
{
UObject* Object = (UObject*)ObjectItem->Object;
// Keep track of how many objects are around.
ObjectCountDuringMarkPhase++;
if (bWithClusters)
{
ObjectItem->ClearFlags(
EInternalObjectFlags::ReachableInCluster);
}
// 1.如果一个object属于RootSet 不 GC
// Object->AddToRoot()
if (ObjectItem->IsRootSet())
{
if (bWithClusters)
{
if (ObjectItem->HasAnyFlags(
EInternalObjectFlags::ClusterRoot)
|| ObjectItem->GetOwnerIndex() > 0)
{
KeepClusterRefsList.Push(ObjectItem);
}
}
LocalObjectsToSerialize.Add(Object);
}
// Regular objects or cluster root objects
else if (!bWithClusters || ObjectItem->GetOwnerIndex() <= 0)
{
bool bMarkAsUnreachable = true;
// 2.如果一个 object 有 Keep 标记位 不 GC
if (ObjectItem->HasAnyFlags(FastKeepFlags))
{
bMarkAsUnreachable = false;
}
else if (!ObjectItem->IsPendingKill() &&
KeepFlags != RF_NoFlags && Object->HasAnyFlags(KeepFlags))
{
bMarkAsUnreachable = false;
}
else if (ObjectItem->IsPendingKill() && bWithClusters &&
ObjectItem->HasAnyFlags(EInternalObjectFlags::ClusterRoot))
{
ClustersToDissolveList.Push(ObjectItem);
}
if (!bMarkAsUnreachable)
{
LocalObjectsToSerialize.Add(Object);
if (bWithClusters)
{
if (ObjectItem->HasAnyFlags(
EInternalObjectFlags::ClusterRoot))
{
KeepClusterRefsList.Push(ObjectItem);
}
}
}
else
{
// 不可达 设置 不可达的标记为
ObjectItem->SetFlags(EInternalObjectFlags::Unreachable);
}
}
}
}
GObjectCountDuringLastMarkPhase.Add(ObjectCountDuringMarkPhase);
}, !bParallel);
}
所有收集到的不能被 GC 的 UObject 都会最终添加到 ObjectsToSerialize 中。然后会调用 ReachabilityAnalysisFunctions 数组中的函数分析:
void PerformReachabilityAnalysis(EObjectFlags KeepFlags,
bool bForceSingleThreaded, bool bWithClusters)
{
FGCArrayStruct* ArrayStruct = FGCArrayPool::Get().GetArrayStructFromPool();
TArray<UObject*>& ObjectsToSerialize = ArrayStruct->ObjectsToSerialize;
/// step 1: 上面的可达性分析
const double StartTime = FPlatformTime::Seconds();
(this->*MarkObjectsFunctions[GetGCFunctionIndex(!bForceSingleThreaded,
bWithClusters)])(ObjectsToSerialize, KeepFlags);
/// step 2:
const double StartTime = FPlatformTime::Seconds();
PerformReachabilityAnalysisOnObjects(ArrayStruct,
bForceSingleThreaded, bWithClusters);
}
收集引用信息
基础类概念介绍
先介绍一下ReferenceToken概念
在UObject体系中,每个类有一个UClass实例用于描述该类的反射信息,使用UProperty可描述每个类的成员变量,但在GC中如果直接遍历UProperty来扫描对象引用关系,效率会比较低(因为存在许多非Object引用型Property),所以UE创建了ReferenceToken,它是一组toke流,描述类中对象的引用情况。
下面代码中列举了引用的类型:
enum EGCReferenceType
{
GCRT_None = 0,
GCRT_Object,
GCRT_Class,
GCRT_PersistentObject,
// Specific reference type token for UObject external package
GCRT_ExternalPackage,
GCRT_ArrayObject,
GCRT_ArrayStruct,
GCRT_FixedArray,
GCRT_AddStructReferencedObjects,
GCRT_AddReferencedObjects,
GCRT_AddTMapReferencedObjects,
GCRT_AddTSetReferencedObjects,
GCRT_AddFieldPathReferencedObject,
GCRT_ArrayAddFieldPathReferencedObject,
GCRT_EndOfPointer,
GCRT_EndOfStream,
GCRT_NoopPersistentObject,
GCRT_NoopClass,
GCRT_ArrayObjectFreezable,
GCRT_ArrayStructFreezable,
GCRT_WeakObject,
GCRT_ArrayWeakObject,
GCRT_LazyObject,
GCRT_ArrayLazyObject,
GCRT_SoftObject,
GCRT_ArraySoftObject,
GCRT_Delegate,
GCRT_ArrayDelegate,
GCRT_MulticastDelegate,
GCRT_ArrayMulticastDelegate,
};
下面是 FGCReferenceInfo 类型定义
- ReturnCount:返回的嵌套深度
- Type:引用的类型,就是 EGCRefenceType
- Offset:这个引用对应的属性在类中的地址偏移
UE巧妙的把这3个信息编码成了一个uint32,因此 FGCReferenceTokenStream 可以通过 TArray
struct FGCReferenceInfo
{
union
{
struct
{
/** Return depth, e.g. 1 for last entry in an array,
2 for last entry in an array of structs of arrays, ... */
uint32 ReturnCount : 8;
/** Type of reference */
// The number of bits needs to match
// TFastReferenceCollector::FStackEntry::ContainerHelperType
uint32 Type : 5;
/** Offset into struct/ object */
uint32 Offset : 19;
};
/** uint32 value of reference info, used for easy conversion
to/ from uint32 for token array */
uint32 Value;
};
};
struct FGCReferenceTokenStream
{
/** Token array */
TArray<uint32> Tokens;
}
下图是我截图的一个堆栈:
这是一个 UMG 测试蓝图 WBP_TestForm_C 初始化 ReferenceTokenStream 时,断点处的内存信息,两个引用 Object 分别是 BtnBack 跟 ImageTest,然后每个 Object 在整个对象的内存空间的偏移地址为:1128 跟 1136,它们嵌套深度都是0。
- Offset : 1136 -> 0100 0111 0000
- Type : 1 -> 0 0001
- ReturnCount : 0 -> 0000 0000
void ProcessObjectArray(FGCArrayStruct& InObjectsToSerializeStruct,
const FGraphEventRef& MyCompletionGraphEvent)
{
while (CurrentIndex < ObjectsToSerialize.Num())
{
CurrentObject = ObjectsToSerialize[CurrentIndex++];
// Get pointer to token stream and jump to the start.
FGCReferenceTokenStream* RESTRICT TokenStream =
&CurrentObject->GetClass()->ReferenceTokenStream;
uint32 TokenStreamIndex = 0;
// Keep track of index to reference info. Used to avoid LHSs.
uint32 ReferenceTokenStreamIndex = 0;
FStackEntry* RESTRICT StackEntry = Stack.GetData();
// 对象的起始地址
uint8* StackEntryData = (uint8*)CurrentObject;
StackEntry->Data = StackEntryData;
StackEntry->ContainerType = GCRT_None;
StackEntry->Stride = 0;
StackEntry->Count = -1;
StackEntry->LoopStartIndex = -1;
// Keep track of token return count in separate integer
// as arrays need to fiddle with it.
int32 TokenReturnCount = 0;
// Parse the token stream.
while (true)
{
////
switch(ReferenceInfo.Type)
{
case GCRT_Object:
case GCRT_Class:
{
// 引用对象的地址: 起始地址 + Offset
UObject** ObjectPtr = (UObject**)(StackEntryData +
ReferenceInfo.Offset);
UObject*& Object = *ObjectPtr;
TokenReturnCount = ReferenceInfo.ReturnCount;
ReferenceProcessor.HandleTokenStreamObjectReference(
NewObjectsToSerialize, CurrentObject, Object,
ReferenceTokenStreamIndex, true);
}
break;
case GCRT_ArrayObject:
{
// We're dealing with an array of object references.
TArray<UObject*>& ObjectArray = *((TArray<UObject*>*)
(StackEntryData + ReferenceInfo.Offset));
TokenReturnCount = ReferenceInfo.ReturnCount;
for (int32 ObjectIndex = 0, ObjectNum = ObjectArray.Num();
ObjectIndex < ObjectNum; ++ObjectIndex)
{
ReferenceProcessor.HandleTokenStreamObjectReference(
NewObjectsToSerialize, CurrentObject,
ObjectArray[ObjectIndex],
ReferenceTokenStreamIndex, true);
}
}
break;
}
}
}
}
最后调用到 HandleObjectReference 对引用的对象设置可达标记位,
FORCEINLINE void HandleObjectReference(TArray<UObject*>& ObjectsToSerialize,
const UObject * const ReferencingObject, UObject*& Object,
const bool bAllowReferenceElimination)
{
const bool IsInPermanentPool =
GUObjectAllocator.ResidesInPermanentPool(Object);
const int32 ObjectIndex = GUObjectArray.ObjectToIndex(Object);
FUObjectItem* ObjectItem = GUObjectArray.IndexToObjectUnsafeForGC(ObjectIndex);
// Remove references to pending kill objects if we're allowed to do so.
if (ObjectItem->IsPendingKill() && bAllowReferenceElimination)
{
///
}
}
清理操作
标记阶段完成后,会进入清理阶段,收集所有不可达的 UObject 整理到全局列表 GUnreachableObjects
void CollectGarbageInternal(EObjectFlags KeepFlags, bool bPerformFullPurge)
{
GatherUnreachableObjects(bForceSingleThreadedGC);
NotifyUnreachableObjects(GUnreachableObjects);
}
bool UnhashUnreachableObjects(bool bUseTimeLimit, float TimeLimit)
{
const double StartTime = FPlatformTime::Seconds();
const int32 TimeLimitEnforcementGranularityForBeginDestroy = 10;
int32 Items = 0;
int32 TimePollCounter = 0;
const bool bFirstIteration = (GUnrechableObjectIndex == 0);
while (GUnrechableObjectIndex < GUnreachableObjects.Num())
{
FUObjectItem* ObjectItem = GUnreachableObjects[GUnrechableObjectIndex++];
{
UObject* Object = static_cast<UObject*>(ObjectItem->Object);
FScopedCBDProfile Profile(Object);
// Begin the object's asynchronous destruction.
Object->ConditionalBeginDestroy();
}
}
}
/// GC 尝试
bool UObject::ConditionalBeginDestroy()
{
#if !UE_BUILD_SHIPPING
if (DebugSpikeMarkAnnotation.Num() > 0)
{
if(!DebugSpikeMarkAnnotation.Get(this))
{
DebugSpikeMarkNames.Add(GetFullName());
}
}
#endif
check(IsValidLowLevel());
if( !HasAnyFlags(RF_BeginDestroyed) )
{
SetFlags(RF_BeginDestroyed);
#if !(UE_BUILD_SHIPPING || UE_BUILD_TEST)
checkSlow(!DebugBeginDestroyed.Contains(this));
DebugBeginDestroyed.Add(this);
#endif
#if PROFILE_ConditionalBeginDestroy
double StartTime = FPlatformTime::Seconds();
#endif
BeginDestroy();
}
}
最终完成 GC 清理。