What can cause objects to break disregard for GC assumptions?

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?

I too am facing kind of similar situation. Any idea how to solve it?

Seems to be a very closely guarded secret as there are no replies… :slight_smile:

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.

Hopefully this helps.

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.

4 Likes

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.

1 Like

i’m having this error from removing widget children

this LogReply quicky remove and create in runtime.

the WeakPtr Owner not erase in whole program.

structure pointer is cames from UAsset’s Memeber property

i set them nullptr when destruct but still encounter gc assumtion error.

+i changed Structure Pointer to Value Copy but still didn’t fixed.

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.