We’re seeing a strange issue with garbage collection that seems to have started when we upgraded to the 4.21 version of the engine. In C++ we are spawning and firing projectiles actors. The code doing this does not store any kind of reference to the projectile actor, however, when the projectiles hit something in the world they add themselves to a TSet of projectiles in save game management code, so their place in the world can be saved out. When this happens, on the next garbage collection pass, the warning on GarbageCollectionVerification.cpp line 91 trips, and then the fatal error at line 131 crashes the game. The ReferencingObject of the projectile actor is NULL. What’s very odd is that if the projectile actor is not added to the savegame TSet, this warning does not fire and the game keeps running. The TSet is a UPROPERTY of a UObject.
Before noticing this, the last time we had built without the editor was with 4.20 (the garbage collection verification code is disabled when WITH_EDITOR is defined) and we didn’t see this behavior then.
Does somebody with more knowledge of the garbage collector know a way that adding a reference to an actor causes it to not be referenced at garbage collection time?
Seems to be a very closely guarded secret as there are no replies…
I had similar issue when playing without editor but also found a solution. The GC finds all referenced objects being collected (no recursion) from all UObject properties and checks that none of these are attached to root, is set to disregard for gc, have owner or object has ClusterRoot flags. I would only focus on the first and possibly the second one.
On my project the problem was that I use singletons for storing common data (which is assigned to the root). When AActor directly referenced one singleton, the GC didn’t like it when it was garbage collected (direct reference). In cases where I have AActor with Components which refer to the singletons, there was no problem (secondary reference). I resolved my use case by changing the direct UPROPERTY hard pointer reference with TWeakObjectPtr and the GC started working with it as expected.
You could check the GarbageCollectionVerification.cpp which has the validation logic.
The short answer is, don’t create UObjects within FEngineLoop::LoadStartupModules().
Early in engine init, from the moment it becomes possible to create UObjects, FUObjectArray::IsOpenForDisregardForGC() will return true. While so, any UObjects created get this special status – FUObjectArray::IsDisregardForGC(Object) will return true for them. They are exempt from garbage collection. Most of them are also automatically rooted.
This special time ends when FEngineLoop::PreInitPostStartupScreen() calls GUObjectArray.CloseDisregardForGC(). After that, normal rules apply.
For objects with Disregard for GC status, these things are true:
They’ll never be garbage collected.
They may not make references to UObjects that are not themselves rooted.
Because these Disregard for GC objects will not be checked for GC, yet are rooted, it’s sort of a paradoxical situation. You’d think their rootedness should protect other objects they make references to – but it won’t. The Disregard for GC objects never go into the reachability analysis, so any references they make won’t make anything Reachable. Also if the thing referred to gets then garbage collected, your UPROPERTY() UObject pointer to it within the Disregard for GC Object won’t be cleared like it normally would, and your pointer will go stale. I guess because of this unexpected behavior, Epic made it an error if you try to make a reference to something that isn’t itself rooted and thus protected from GC.
Thanks for this clarification, it helped us figure out why this was happening in our project. In our case we were using ConstructorHelpers::FClassFinder in a C++ constructor, which gets loaded during OpenForDisregardForGC. Simply removing it and loading assets properly via UPROPERTY was what was needed.
My solution for my projects was - to declare all pointers and non-simple data types (not with int32, float and so on) in the header file at least with UPROPERTY() or UPROPERTY(Transient).
Solved- my problem was my UAsset has UPROPERTY transient pointer and it has my game instance.
that shouldn’t collect garbage.
so i changed that property to TWeakObjPtr
and Widget’s member wasn’t have problem.