What can cause objects to break disregard for GC assumptions?

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.

3 Likes