淘先锋技术网

首页 1 2 3 4 5 6 7

源码版本: 4.22.3

 

一:自动GC部分

void UWorld::Tick( ELevelTick TickType, float DeltaSeconds )
{
    //略

	GEngine->ConditionalCollectGarbage();

    //略
}

下面将conditionCollectGarbage 方法自己的理解通过注释的方式记录。

void UEngine::ConditionalCollectGarbage()
{
        //防止同一帧多次进入
	if (GFrameCounter != LastGCFrame)
	{
                //略

                //如果是全量GC
			if (bFullPurgeTriggered)
			{
                    //全量GC
				if (TryCollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS, true))
				{
                        //遍历level cleanup nullptr
					ForEachObjectOfClass(UWorld::StaticClass(),[](UObject* World)
					{
						CastChecked<UWorld>(World)->CleanupActors();
					});

                        //reset flag
					bFullPurgeTriggered = false;
					bShouldDelayGarbageCollect = false;
					TimeSinceLastPendingKillPurge = 0.0f;
				}
			}
                //增量GC
			else
			{
				bool bHasAWorldBegunPlay = false;
				ForEachObjectOfClass(UWorld::StaticClass(), [&bHasAWorldBegunPlay](UObject* World)
				{
					bHasAWorldBegunPlay = bHasAWorldBegunPlay || CastChecked<UWorld>(World)->HasBegunPlay();
				});
                
                    //如果 world beginplay
				if (bHasAWorldBegunPlay)
				{
					TimeSinceLastPendingKillPurge += FApp::GetDeltaTime();
                    
                        //自动GC间隔时间,可配置gc.TimeBetweenPurgingPendingKillObjects
					const float TimeBetweenPurgingPendingKillObjects =     
                        //并且会根据内存大小等情况动态调整,默认60s
                    GetTimeBetweenGarbageCollectionPasses();

					    // See if we should delay garbage collect for this frame
					if (bShouldDelayGarbageCollect)
					{
                            //某些情况下会跳过
						bShouldDelayGarbageCollect = false;
					}
				    	// 如果没有正在进行的回收
					else if (!IsIncrementalPurgePending()
                             //并且 从上次回收已经超过了自动gc间隔
						&& (TimeSinceLastPendingKillPurge > TimeBetweenPurgingPendingKillObjects) && TimeBetweenPurgingPendingKillObjects > 0.f)
					{
                            //增量得进行一次标记 清扫
						SCOPE_CYCLE_COUNTER(STAT_GCMarkTime);
						PerformGarbageCollectionAndCleanupActors();
					}
					else
					{
                            //继续增量回收未完成得
						SCOPE_CYCLE_COUNTER(STAT_GCSweepTime);
						IncrementalPurgeGarbage(true);
					}
				}
			}
        
            //如果gc.CollectGarbageEveryFrame配置了>0, 则每帧都执行全量GC(用于调试)
		if (CVarCollectGarbageEveryFrame.GetValueOnGameThread() > 0)
		{
			ForceGarbageCollection(true);
		}

		LastGCFrame = GFrameCounter;
	}
}
bool TryCollectGarbage(EObjectFlags KeepFlags, bool bPerformFullPurge)
{
	// 尝试获取gc锁
	bool bCanRunGC = FGCCSyncObject::Get().TryGCLock();
    if (!bCanRunGC)
	{
        //gc.NumRetriesBeforeForcingGC 默认10可配置  跳过gc次数
		if (GNumRetriesBeforeForcingGC > 0 && GNumAttemptsSinceLastGC > GNumRetriesBeforeForcingGC)
		{
			// Force GC and block main thread			
			UE_LOG(LogGarbage, Warning, TEXT("TryCollectGarbage: forcing GC after %d skipped attempts."), GNumAttemptsSinceLastGC);
            //强制gc reset flag
			GNumAttemptsSinceLastGC = 0;
			AcquireGCLock();
			bCanRunGC = true;
		}
	}
	if (bCanRunGC)
	{
		// Perform actual garbage collection
        //垃圾回收 bPerformFullPurge:是否进行一次全量的回收
		CollectGarbageInternal(KeepFlags, bPerformFullPurge);

		// Other threads are free to use UObjects
		ReleaseGCLock();
	}
	else
	{
		GNumAttemptsSinceLastGC++;
	}

	return bCanRunGC;
}
void UEngine::PerformGarbageCollectionAndCleanupActors()
{
	// We don't collect garbage while there are outstanding async load requests as we would need
	// to block on loading the remaining data.
	if (!IsAsyncLoading())
	{
		// Perform housekeeping. 增量
		if (TryCollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS, false))
		{
			ForEachObjectOfClass(UWorld::StaticClass(), [](UObject* World)
			{
				CastChecked<UWorld>(World)->CleanupActors();
			});

			// Reset counter.
			TimeSinceLastPendingKillPurge = 0.0f;
			bFullPurgeTriggered = false;
			LastGCFrame = GFrameCounter;
		}
	}
}

另外的:

void UEngine::ForceGarbageCollection(bool bForcePurge/*=false*/)
{
	TimeSinceLastPendingKillPurge = 1.0f + GetTimeBetweenGarbageCollectionPasses();
	bFullPurgeTriggered = bFullPurgeTriggered || bForcePurge;
}

通过这个方法,显示在下一帧触发一次自动gc(并不一定,只是try)。

 

二:手动GC部分 todo

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)
{
	// Reset GC skip counter
	GNumAttemptsSinceLastGC = 0;

	// Flush streaming before GC if requested
    //gc.FlushStreamingOnGC 配置 是否flush异步加载流
	if (GFlushStreamingOnGC)
	{
		if (IsAsyncLoading())
		{
            //log
		}
		FGCCSyncObject::Get().GCUnlock();
		FlushAsyncLoading();
		FGCCSyncObject::Get().GCLock();
	}

    //pre delegate broadcast
	FCoreUObjectDelegates::GetPreGarbageCollectDelegate().Broadcast();

	GLastGCFrame = GFrameCounter;

	{
		FGCScopeLock GCLock;

        
        //如果需要一次完全gc或者之前的增量gc未完成,全量gc一次
		if (GObjIncrementalPurgeIsInProgress || GObjPurgeIsRequired)
		{
			IncrementalPurgeGarbage(false);
			FMemory::Trim();
		}
        
        //略
        
        
        //判断是否强制单线程gc
		const bool bForceSingleThreadedGC = !FApp::ShouldUseThreadingForPerformance() || !FPlatformProcess::SupportsMultithreading() ||
#if PLATFORM_SUPPORTS_MULTITHREADED_GC
		(FPlatformMisc::NumberOfCores() < 2 || GAllowParallelGC == 0 || PERF_DETAILED_PER_CLASS_GC_STATS);
#else	//PLATFORM_SUPPORTS_MULTITHREADED_GC
			true;
#endif	//PLATFORM_SUPPORTS_MULTITHREADED_GC




		// Perform reachability analysis.
        //可达性分析
		{
			const double StartTime = FPlatformTime::Seconds();
			FRealtimeGC TagUsedRealtimeGC;
			TagUsedRealtimeGC.PerformReachabilityAnalysis(KeepFlags, bForceSingleThreadedGC);
		}
        

		// Reconstruct clusters if needed
        //回收一些cluster?
		if (GUObjectClusters.ClustersNeedDissolving())
		{
			const double StartTime = FPlatformTime::Seconds();
			GUObjectClusters.DissolveClusters();
		}

		// Fire post-reachability analysis hooks
		FCoreUObjectDelegates::PostReachabilityAnalysis.Broadcast();

		{
            //清除弱引用 (增量或全量)
			FGCArrayPool::Get().ClearWeakReferences(bPerformFullPurge);
            
            //收集 达不到的object
			GatherUnreachableObjects(bForceSingleThreadedGC);

			if (bPerformFullPurge || !GIncrementalBeginDestroyEnabled)
			{
				UnhashUnreachableObjects(/**bUseTimeLimit = */ false);
				FScopedCBDProfile::DumpProfile();
			}
		}

		// Set flag to indicate that we are relying on a purge to be performed.
		GObjPurgeIsRequired = true;
		// Reset purged count.
		GPurgedObjectCountSinceLastMarkPhase = 0;
		GObjCurrentPurgeObjectIndexResetPastPermanent = true;

		// Perform a full purge by not using a time limit for the incremental purge. The Editor always does a full purge.
        //编辑器 使用全量回收
		if (bPerformFullPurge || GIsEditor)
		{
			IncrementalPurgeGarbage(false);
		}
        
        //压缩 uobject 的哈希表
		if (bPerformFullPurge)
		{
			ShrinkUObjectHashTables();
		}

		// Destroy all pending delete linkers
		DeleteLoaders();

		// Trim allocator memory
		FMemory::Trim();
	}

	// Route callbacks to verify GC assumptions
	FCoreUObjectDelegates::GetPostGarbageCollect().Broadcast();

	STAT_ADD_CUSTOMMESSAGE_NAME( STAT_NamedMarker, TEXT( "GarbageCollection - End" ) );
}
void IncrementalPurgeGarbage( bool bUseTimeLimit, float TimeLimit )
{
    //Shut down the object manager
	if (GExitPurge)
	{
		GObjPurgeIsRequired = true;
		GUObjectArray.DisableDisregardForGC();
		GObjCurrentPurgeObjectIndexNeedsReset = true;
		GObjCurrentPurgeObjectIndexResetPastPermanent = false;
	}
	// Early out if there is nothing to do.
	if( !GObjPurgeIsRequired )
	{
		return;
	}

	bool bCompleted = false;
    
    //設置flag 利用构造和析构 在多线程环境下
	struct FResetPurgeProgress
	{
		bool& bCompletedRef;
		FResetPurgeProgress(bool& bInCompletedRef)
			: bCompletedRef(bInCompletedRef)
		{
			// Incremental purge is now in progress.
			GObjIncrementalPurgeIsInProgress = true;
			FPlatformMisc::MemoryBarrier();
		}
		~FResetPurgeProgress()
		{
			if (bCompletedRef)
			{
				GObjIncrementalPurgeIsInProgress = false;
				FPlatformMisc::MemoryBarrier();
			}
		}

	} ResetPurgeProgress(bCompleted);

	// Keep track of start time to enforce time limit unless bForceFullPurge is true;
	GCStartTime							= FPlatformTime::Seconds();
	bool		bTimeLimitReached							= false;
	// Depending on platform FPlatformTime::Seconds might take a noticeable amount of time if called thousands of times so we avoid
	// enforcing the time limit too often, especially as neither Destroy nor actual deletion should take significant
	// amounts of time.
	const int32	TimeLimitEnforcementGranularityForDestroy	= 10;
	const int32	TimeLimitEnforcementGranularityForDeletion	= 100;

	if (GUnrechableObjectIndex < GUnreachableObjects.Num())
	{
		{
			FConditionalGCLock ScopedGCLock;
            //遍历unreached object begin destroy -> finish destroy (增量或全量)
            //调用begin destroy(LowLevelRename(NAME_None) && SetLinker( NULL, INDEX_NONE                 
            //在派生类中可能会处理一些 异步资源等等 ) )
			bTimeLimitReached = UnhashUnreachableObjects(bUseTimeLimit, TimeLimit);
		}
		if (GUnrechableObjectIndex >= GUnreachableObjects.Num())
		{
			FScopedCBDProfile::DumpProfile();
		}
	}

	FGCScopeLock GCLock;

	if( !GObjFinishDestroyHasBeenRoutedToAllObjects && !bTimeLimitReached )
	{
		check(GUnrechableObjectIndex >= GUnreachableObjects.Num());

		int32 TimeLimitTimePollCounter = 0;
		int32 FinishDestroyTimePollCounter = 0;

		if (GObjCurrentPurgeObjectIndexNeedsReset)
		{
			// iterators don't have an op=, so we destroy it and reconstruct it with a placement new
			// GObjCurrentPurgeObjectIndex	= FRawObjectIterator(GObjCurrentPurgeObjectIndexResetPastPermanent);
			GObjCurrentPurgeObjectIndex.~FRawObjectIterator();
			new (&GObjCurrentPurgeObjectIndex) FRawObjectIterator(GObjCurrentPurgeObjectIndexResetPastPermanent);
			GObjCurrentPurgeObjectIndexNeedsReset = false;
		}
        

        //当前清除对象的迭代器 
		while( GObjCurrentPurgeObjectIndex )
		{
			FUObjectItem* ObjectItem = *GObjCurrentPurgeObjectIndex;


			if (ObjectItem->IsUnreachable())
			{
				UObject* Object = static_cast<UObject*>(ObjectItem->Object);

				if(Object->IsReadyForFinishDestroy())
				{
					Object->ConditionalFinishDestroy();
				}
				else
				{
                    //没有准备好的object
                    
					GGCObjectsPendingDestruction.Add(Object);
					GGCObjectsPendingDestructionCount++;
				}
			}

			++GObjCurrentPurgeObjectIndex;


			const bool bPollTimeLimit = ((TimeLimitTimePollCounter++) % TimeLimitEnforcementGranularityForDestroy == 0);
			if( bUseTimeLimit && bPollTimeLimit && ((FPlatformTime::Seconds() - GCStartTime) > TimeLimit) )
			{
				bTimeLimitReached = true;
				break;
			}
		}

		if( !GObjCurrentPurgeObjectIndex )
		{
			//defer 遍历的Uobject
			int32 LastLoopObjectsPendingDestructionCount = GGCObjectsPendingDestructionCount;
			while( GGCObjectsPendingDestructionCount > 0 )
			{
				int32 CurPendingObjIndex = 0;
				while( CurPendingObjIndex < GGCObjectsPendingDestructionCount )
				{
					UObject* Object = GGCObjectsPendingDestruction[ CurPendingObjIndex ];

					if( Object->IsReadyForFinishDestroy() )
					{
						const FName& ClassName = Object->GetClass()->GetFName();
						int32 InstanceCount = GClassToPurgeCountMap.FindRef( ClassName );
						GClassToPurgeCountMap.Add( ClassName, ++InstanceCount );
						Object->ConditionalFinishDestroy();
						{
							GGCObjectsPendingDestruction[ CurPendingObjIndex ] = GGCObjectsPendingDestruction[ GGCObjectsPendingDestructionCount - 1 ];
							GGCObjectsPendingDestructionCount--;
						}
					}
					else
					{
						CurPendingObjIndex++;
					}

					const bool bPollTimeLimit = ((TimeLimitTimePollCounter++) % TimeLimitEnforcementGranularityForDestroy == 0);
					if( bUseTimeLimit && bPollTimeLimit && ((FPlatformTime::Seconds() - GCStartTime) > TimeLimit) )
					{
						bTimeLimitReached = true;
						break;
					}
				}

				if( bUseTimeLimit )
				{
					// A time limit is set and we've completed a full iteration over all leftover objects, so
					// go ahead and bail out even if we have more time left or objects left to process.  It's
					// likely in this case that we're waiting for the render thread.
					break;
				}
				else if( GGCObjectsPendingDestructionCount > 0 )
				{
					if (FPlatformProperties::RequiresCookedData())
					{
						const bool bPollTimeLimit = ((FinishDestroyTimePollCounter++) % TimeLimitEnforcementGranularityForDestroy == 0);
#if PLATFORM_IOS
                        const double MaxTimeForFinishDestroy = 30.0;
#else
						const double MaxTimeForFinishDestroy = 10.0;
#endif
						// Check if we spent too much time on waiting for FinishDestroy without making any progress
						if (LastLoopObjectsPendingDestructionCount == GGCObjectsPendingDestructionCount && bPollTimeLimit &&
							((FPlatformTime::Seconds() - GCStartTime) > MaxTimeForFinishDestroy))
						{
							UE_LOG(LogGarbage, Warning, TEXT("Spent more than %.2fs on routing FinishDestroy to objects (objects in queue: %d)"), MaxTimeForFinishDestroy, GGCObjectsPendingDestructionCount);
							UObject* LastObjectNotReadyForFinishDestroy = nullptr;
							for (int32 ObjectIndex = 0; ObjectIndex < GGCObjectsPendingDestructionCount; ++ObjectIndex)
							{
								UObject* Obj = GGCObjectsPendingDestruction[ObjectIndex];
								bool bReady = Obj->IsReadyForFinishDestroy();
								UE_LOG(LogGarbage, Warning, TEXT("  [%d]: %s, IsReadyForFinishDestroy: %s"),
									ObjectIndex,
									*GetFullNameSafe(Obj),
									bReady ? TEXT("true") : TEXT("false"));
								if (!bReady)
								{
									LastObjectNotReadyForFinishDestroy = Obj;
								}
							}

#if PLATFORM_DESKTOP
							ensureMsgf(0, TEXT("Spent to much time waiting for FinishDestroy for %d object(s) (last object: %s), check log for details"),
								GGCObjectsPendingDestructionCount,
								*GetFullNameSafe(LastObjectNotReadyForFinishDestroy));
#else
							//for non-desktop platforms, make this a warning so that we can die inside of an object member call.
							//this will give us a greater chance of getting useful memory inside of the platform minidump.
							UE_LOG(LogGarbage, Warning, TEXT("Spent to much time waiting for FinishDestroy for %d object(s) (last object: %s), check log for details"),
								GGCObjectsPendingDestructionCount,
								*GetFullNameSafe(LastObjectNotReadyForFinishDestroy));
							if (LastObjectNotReadyForFinishDestroy)
							{
								LastObjectNotReadyForFinishDestroy->AbortInsideMemberFunction();
							}
							else
							{
								//go through the standard fatal error path if LastObjectNotReadyForFinishDestroy is null.
								//this could happen in the current code flow, in the odd case where an object finished readying just in time for the loop above.
								UE_LOG(LogGarbage, Fatal, TEXT("LastObjectNotReadyForFinishDestroy is NULL."));
							}
#endif
						}
					}
					// Sleep before the next pass to give the render thread some time to release fences.
					FPlatformProcess::Sleep( 0 );
				}

				LastLoopObjectsPendingDestructionCount = GGCObjectsPendingDestructionCount;
			}

			// Have all objects been destroyed now?
			if( GGCObjectsPendingDestructionCount == 0 )
			{
				// Release memory we used for objects pending destruction, leaving some slack space
				GGCObjectsPendingDestruction.Empty( 256 );

				// Destroy has been routed to all objects so it's safe to delete objects now.
				GObjFinishDestroyHasBeenRoutedToAllObjects = true;
				GObjCurrentPurgeObjectIndexNeedsReset = true;
				GObjCurrentPurgeObjectIndexResetPastPermanent = !GExitPurge;
			}
		}
	}		

	if( GObjFinishDestroyHasBeenRoutedToAllObjects && !bTimeLimitReached )
	{
		// Perform actual object deletion.
		// @warning: Can't use FObjectIterator here because classes may be destroyed before objects.
		int32 ProcessCount = 0;
		if (GObjCurrentPurgeObjectIndexNeedsReset)
		{
			// iterators don't have an op=, so we destroy it and reconstruct it with a placement new
			// GObjCurrentPurgeObjectIndex	= FRawObjectIterator(GObjCurrentPurgeObjectIndexResetPastPermanent);
			GObjCurrentPurgeObjectIndex.~FRawObjectIterator();
			new (&GObjCurrentPurgeObjectIndex) FRawObjectIterator(GObjCurrentPurgeObjectIndexResetPastPermanent);
			GObjCurrentPurgeObjectIndexNeedsReset = false;
		}
		while( GObjCurrentPurgeObjectIndex )
		{
			//@todo UE4 - A prefetch was removed here. Re-add it. It wasn't right anyway, since it was ten items ahead and the consoles on have 8 prefetch slots

			FUObjectItem* ObjectItem = *GObjCurrentPurgeObjectIndex;
			checkSlow(ObjectItem);
			if (ObjectItem->IsUnreachable())
			{
				UObject* Object = (UObject*)ObjectItem->Object;
				check(Object->HasAllFlags(RF_FinishDestroyed|RF_BeginDestroyed));
				GIsPurgingObject				= true; 
				Object->~UObject();
				GUObjectAllocator.FreeUObject(Object);
				GIsPurgingObject				= false;
				// Keep track of purged stats.
				GPurgedObjectCountSinceLastMarkPhase++;
			}

			// Advance to the next object.
			++GObjCurrentPurgeObjectIndex;

			ProcessCount++;

			// Only check time limit every so often to avoid calling FPlatformTime::Seconds too often.
			if( bUseTimeLimit && (ProcessCount == TimeLimitEnforcementGranularityForDeletion))
			{
				if ((FPlatformTime::Seconds() - GCStartTime) > TimeLimit)
				{
					bTimeLimitReached = true;
					break;
				}
				ProcessCount = 0;
			}
		}

		if( !GObjCurrentPurgeObjectIndex )
		{
			bCompleted = true;
			// Incremental purge is finished, time to reset variables.
			GObjFinishDestroyHasBeenRoutedToAllObjects		= false;
			GObjPurgeIsRequired								= false;
			GObjCurrentPurgeObjectIndexNeedsReset			= true;
			GObjCurrentPurgeObjectIndexResetPastPermanent	= true;

			// Log status information.
			UE_LOG(LogGarbage, Log, TEXT("GC purged %i objects (%i -> %i)"), GPurgedObjectCountSinceLastMarkPhase, GObjectCountDuringLastMarkPhase.GetValue(), GObjectCountDuringLastMarkPhase.GetValue() - GPurgedObjectCountSinceLastMarkPhase );
#if PERF_DETAILED_PER_CLASS_GC_STATS
			LogClassCountInfo( TEXT("objects of"), GClassToPurgeCountMap, 10, GPurgedObjectCountSinceLastMarkPhase );
#endif
		}
	}
}
//可达性分析
void PerformReachabilityAnalysis(EObjectFlags KeepFlags, bool bForceSingleThreaded = false)
	{

		/** Growing array of objects that require serialization */
		FGCArrayStruct* ArrayStruct = FGCArrayPool::Get().GetArrayStructFromPool();
		TArray<UObject*>& ObjectsToSerialize = ArrayStruct->ObjectsToSerialize;

		// Reset object count.
		GObjectCountDuringLastMarkPhase.Reset();

		// Make sure GC referencer object is checked for references to other objects even if it resides in permanent object pool
        //非UObject体系的类 归入gc的地方
		if (FPlatformProperties::RequiresCookedData() && FGCObject::GGCObjectReferencer && GUObjectArray.IsDisregardForGC(FGCObject::GGCObjectReferencer))
		{
			ObjectsToSerialize.Add(FGCObject::GGCObjectReferencer);
		}

		{
            //将所有物体标为不可达
			MarkObjectsAsUnreachable(ObjectsToSerialize, KeepFlags, bForceSingleThreaded);
		}

		{
            //可达性分析
			PerformReachabilityAnalysisOnObjects(ArrayStruct, bForceSingleThreaded);
		}
        

		FGCArrayPool::Get().ReturnToPool(ArrayStruct);

	}

标记阶段首先将所有物体标为不可达,然后根据引用关系标记。标记时运用了记号流的方式,分析序列化时object拥有的token stream 根据不同的类型找出引用关系标记。 (多线程处理和 cluster优化)

大量粒子等可以使用cluster,加速清扫过程。