I was recently testing two ways of avoiding UObjects garbage collection :
- Always mark all UObjects as UPROPERTY(), make their containing class inherit from UObject and also mark it as UPROPERTY() at higher level, etc…
- Do not mark UObjects as UPROPERTY(), make their containing class inherit from FGCObject and implement AddReferencedObjects() to add all needed UObjects references to the collector.
Is there any preferred way to achieve this among those ? Is there any overhead of using one or another ?
to permanently prevent objects from getting processed during GC you can add them to the root set via UObjectBaseUtility::AddToRoot / UObjectBaseUtility::RemoveFromRoot. You need to take care of their lifecycle though to prevent memory leaks by forgetting to remove them from the root set again.
Under the hood, GC starts by marking all UObjects as Unreachable unless they are in the SerializedObjects array which anything in that array is treated as a Root object and not ever GC’d.
Important: If a UObject is not a root set object, and no root object references a non-root set object, then that object marked as Unreachable will never have the Unreachable flag removed during the GC pass and it will get collected and destroyed as an orphaned object. All UObject must have a root set object referencing them somewhere or they are GC’d.
FGCObject is a base class specifically designed to add GC behavior to classes that are not derived from UObject to begin with. But those will still be GC’d unless you add them to referenced objects array (which makes them go in to SerializedObjects temporarily to treat them as if they were root objects). so they won’t GC OR a root set object references your FGCObject.
Setting a UObject as RootSet makes it also be treated as a root object as long as that flag stays set, and they will not GC until you remove the RootSet flag.
Easiest of all is to just make sure your UObject is referenced through a UPROPERTY (from a root set object in the reference chain!)
In order for any UObject to NOT GC, it needs to either BE a root set object, or be referenced from a root set object somewhere in the reference hierarchy.
Any UObject that has no root object referencing it anymore is orphaned and will GC, which is how GC works.
So knowing that, just pick any of those solutions, but pick the one that is cleanest and makes the most sense in the code you are adding.
Thank you for your answer !
Would this be the preferred solution ? Do you know if there are any downsides to using the others I mentionned (in particular the one with the FGCObject) ?
James covered it very extensively but I just want to note that as far as GC performance goes UPROPERTY references are usually faster to traverse than calling AddReferencedObjects functions.
Thanks a lot for this detailed answer !