voidCollectGarbage(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(); }
/** Objects flags for internal use (GC, low level UObject code) */ enum classEInternalObjectFlags : int32 { None = 0,
LoaderImport = 1 << 20, ///< Object is ready to be imported by another package during loading Garbage = 1 << 21, ///< Garbage from logical point of view and should not be referenced. This flag is mirrored in EObjectFlags as RF_Garbage for performance PersistentGarbage = 1 << 22, ///< Same as above but referenced through a persistent reference so it can't be GC'd ReachableInCluster = 1 << 23, ///< External reference to object in cluster exists ClusterRoot = 1 << 24, ///< Root of a cluster Native = 1 << 25, ///< Native (UClass only). Async = 1 << 26, ///< Object exists only on a different thread than the game thread. AsyncLoading = 1 << 27, ///< Object is being asynchronously loaded. Unreachable = 1 << 28, ///< Object is not reachable on the object graph. PendingKill UE_DEPRECATED(5.0, "PendingKill flag should no longer be used. Use Garbage flag instead.") = 1 << 29, ///< Objects that are pending destruction (invalid for gameplay but valid objects). This flag is mirrored in EObjectFlags as RF_PendingKill for performance RootSet = 1 << 30, ///< Object will not be garbage collected, even if unreferenced. PendingConstruction = 1 << 31, ///< Object didn't have its class constructor called yet (only the UObjectBase one to initialize its most basic members)
/** * Deletes all unreferenced objects, keeping objects that have any of the passed in KeepFlags set * * @param KeepFlags objects with those flags will be kept regardless of being referenced or not * @param bPerformFullPurge if true, perform a full purge after the mark pass */ voidCollectGarbageInternal(EObjectFlags KeepFlags, bool bPerformFullPurge) { // ... // Perform reachability analysis. { constdouble StartTime = FPlatformTime::Seconds(); FRealtimeGC TagUsedRealtimeGC;
TagUsedRealtimeGC.PerformReachabilityAnalysis(KeepFlags, Options); UE_LOG(LogGarbage, Log, TEXT("%f ms for GC"), (FPlatformTime::Seconds() - StartTime) * 1000); }
FGCArrayPool& ArrayPool = FGCArrayPool::Get(); TArray<FGCArrayStruct*> AllArrays; ArrayPool.GetAllArrayStructsFromPool(AllArrays); // This needs to happen before clusters get dissolved otherwisise cluster information will be missing from history ArrayPool.UpdateGCHistory(AllArrays);
// This needs to happen after NotifyGarbageReferencers and GatherUnreachableObjects since both can mark more objects as unreachable ArrayPool.ClearWeakReferences(AllArrays);
// Now return arrays back to the pool and free some memory if requested for (int32 Index = 0; Index < AllArrays.Num(); ++Index) { FGCArrayStruct* ArrayStruct = AllArrays[Index]; if (bPerformFullPurge || Index % 7 == 3) // delete 1/7th of them just to keep things from growing too much between full purges { ArrayPool.FreeArrayStruct(ArrayStruct); } else { ArrayPool.ReturnToPool(ArrayStruct); } }
// Make sure nothing will be using potentially freed arrays AllArrays.Empty();
/** * Performs reachability analysis. * * @param KeepFlags Objects with these flags will be kept regardless of being referenced or not */ voidPerformReachabilityAnalysis(EObjectFlags KeepFlags, const EFastReferenceCollectorOptions InOptions) { LLM_SCOPE(ELLMTag::GC);
// Make sure GC referencer object is checked for references to other objects even if it resides in permanent object pool if (FPlatformProperties::RequiresCookedData() && FGCObject::GGCObjectReferencer && GUObjectArray.IsDisregardForGC(FGCObject::GGCObjectReferencer)) { ObjectsToSerialize.Add(FGCObject::GGCObjectReferencer); }
{ constdouble StartTime = FPlatformTime::Seconds(); // Mark phase doesn't care about PendingKill being enabled or not so there's just fewer compiled in functions const EFastReferenceCollectorOptions OptionsForMarkPhase = InOptions & ~EFastReferenceCollectorOptions::WithPendingKill; (this->*MarkObjectsFunctions[GetGCFunctionIndex(OptionsForMarkPhase)])(ObjectsToSerialize, KeepFlags); UE_LOG(LogGarbage, Verbose, TEXT("%f ms for MarkObjectsAsUnreachable Phase (%d Objects To Serialize)"), (FPlatformTime::Seconds() - StartTime) * 1000, ObjectsToSerialize.Num()); }
{ constdouble StartTime = FPlatformTime::Seconds(); PerformReachabilityAnalysisOnObjects(ArrayStruct, InOptions); UE_LOG(LogGarbage, Verbose, TEXT("%f ms for Reachability Analysis"), (FPlatformTime::Seconds() - StartTime) * 1000); } // Allowing external systems to add object roots. This can't be done through AddReferencedObjects // because it may require tracing objects (via FGarbageCollectionTracer) multiple times FCoreUObjectDelegates::TraceExternalRootsForReachabilityAnalysis.Broadcast(*this, KeepFlags, !(InOptions & EFastReferenceCollectorOptions::Parallel));
// Make sure GC referencer object is checked for references to other objects even if it resides in permanent object pool if (FPlatformProperties::RequiresCookedData() && FGCObject::GGCObjectReferencer && GUObjectArray.IsDisregardForGC(FGCObject::GGCObjectReferencer)) { ObjectsToSerialize.Add(FGCObject::GGCObjectReferencer); }
{ constdouble StartTime = FPlatformTime::Seconds(); // Mark phase doesn't care about PendingKill being enabled or not so there's just fewer compiled in functions const EFastReferenceCollectorOptions OptionsForMarkPhase = InOptions & ~EFastReferenceCollectorOptions::WithPendingKill; (this->*MarkObjectsFunctions[GetGCFunctionIndex(OptionsForMarkPhase)])(ObjectsToSerialize, KeepFlags); UE_LOG(LogGarbage, Verbose, TEXT("%f ms for MarkObjectsAsUnreachable Phase (%d Objects To Serialize)"), (FPlatformTime::Seconds() - StartTime) * 1000, ObjectsToSerialize.Num()); }
/** Pointers to functions used for Marking objects as unreachable */ MarkObjectsFn MarkObjectsFunctions[4]; /** Pointers to functions used for Reachability Analysis */ ReachabilityAnalysisFn ReachabilityAnalysisFunctions[8];