Documentation is very scarce about engine internals

Documentation is very helpful in ‘how to use the engine’ or ‘how to develop a plugin’, but when it comes to some engine internals, it’s nearly non-existence.
Typically I find no GC-internal information in any documentation, doc only tells you what is a GC and how to use a GC, but what I need is ‘how the GC works, does it handle cyclic reference, does it handle on-stack root reference’
And this forumn seems lack of Epic developers, since I asked about GC internals for weeks, more than 100 views, but not even 1 answer

Thanks for your question, qiqian82. We are working on some expansion of the GC documentation, but it might not go to the degree of technical depth you are describing in the immediate future. I’m not sure exactly what you’re asking about on-stack root references, but if you can clarify it, I can get you an answer. As for cyclic references, the reflection system generally handles them before they get to GC. For example, if I create two actors in a level that point to each other via a BP variable, storage class pointer (TArray, TWeakPtr, etc.), or UPROPERTY, then call Destroy() on one of them, it will be destroyed. The other actor’s pointer variable will be set to NULL in this process. When GC comes around, there is no longer a cycle because of the pointer being set to NULL, so the destroyed actor can be freed. GC in general is designed to work without the user getting involved in it, but for some projects it may be helpful. If there’s a more specific question you have, I can look into it for you, but as I mentioned earlier, there are some GC documentation updates in production.

Thanks for your explanations.

So if I declare 2 UObject subclasses with refereances to each other, one of them should be TWeakPtr
What if I create 2 blueprint class, which have references to each other ? I couldn’t find a way to declare variables in blueprint to be “Weak”

Now about the “root”. Typically GC scans object references from some "root"s, like static variables in classes, variables in thread-stack, it’s a recursive process until all reachable objects are scanned, all remaining unscanned objects are garbage.
What I see in UE4 is, UE4 allocate all objects into a big array, some objects might be flagged as “root”, when UE4 try to collect garbage, it scans the whole array, not only “root” objects in this array. Do so means, it will scan into unreachable objects, it’s a wasting of CPU cycles, also bring trouble to handle cyclic references.

My previous post may not have made this clear, but a UPROPERTY UObject* is a weak pointer and is visible to GC, so you don’t need to use TWeakPtr explicitly. If you make something like a BP actor with a BP variable that’s a pointer to another AActor (etc.) in a blueprint, the example in my first reply will work as described.

Our GC system does use an array of objects. We scan over the array to identify flagged “root” objects, as you said, and collect them for further processing. We also take this opportunity to mark all non-root objects as “unreachable”, which is necessary in order to know which ones to delete later. With this done, we can process roots and their references, removing the “unreachable” flag as we go. This process handles cyclical references between unreachable objects because the entire unreachable cycle will not be touched, and thus will be left with the “unreachable” flag set on all the objects, meaning it will be marked for deletion when the process ends.
If you do have a case where GC exhibits a bug, though, we would love to know about it and will file a ticket to get it fixed. Thank you!

1 Like

I read the source code again, now I understands how it handles roots.
One more question, if I created a new thread, and use NewObject to create a variable on the thread’s stack, it will be GCed, since stack is not considered by GC, so I have to mark it as root ?

If I remember correctly, there was a blog article on unrealengine.com on the garbage collector and how it works etc. I can not seem to find it anymore (so I am not even sure if I remember correctly), but maybe someone else has a hunch? It was quite lengthy, but I did not read it yet…
That article might at least proove useful to you.

I’m not totally clear on what you’re trying to do. In general, though, if you create a new UObject and then don’t store it in any reflected pointer/reference and don’t mark it as root, it will likely be considered orphaned and thus a candidate for deletion.
@rYuxq: There is some updated documentation on GC in the Actor Lifecycle page as well: Unreal Engine Actor Lifecycle | Unreal Engine 5.3 Documentation
I’m not sure about the wiki, but it looks like there’s some information there based on a quick google search.