GC messing up TArray without UPROPERTY

I don’t know if this is intended, but in my eyes this is a bug.

SHORT

TArray members without a UPROPERTY macro will get GC’ed to oblivion. And when I say GC’ed to oblivion, I mean not in the nice clean way with .Num() == 0, but in the bad and dirty way with .Num() > 0 and the iterator returning pointers to another spacetime continuum.

LONG

I created a C++ GameMode class and put into it a TArray as its member. Pretty basic stuff. The problem is I left it without a UPROPERTY macro. Result: At a random time the game would crash. In the debug during a crash I could always see that the content of the TArray would be messed up and iterating over it would give me crazy pointers to another galaxy. After days trying to find out what I did wrong and checking my custom classes (which in the process I converted to UObject derived ones in hope of success), blueprints and possible race conditions, I finally added a ForceGarbageCollection(true) to the Tick() function of my GameMode and BAM the game started crashing instantly instead of randomly. After that I checked all my .h again and found this TArray without a UPROPERTY macro. So I added it just in case, and soon after the angels started to sing as my game ran in shining glory unharmed by the tentacles of the GC.

Hi SyMutex,

I was attempting to reproduce your crash in the most simple way possible but I can’t get it to occur. Are you doing anything with this TArray in code past simply creating it and then calling Garbage Collection on tick?

Sure… I was adding instances of my UObject derived custom class. Actually the problem happened after adding one instance of my custom class and forcing the GC. The next time I try to add another instance the array was already damaged. Let me try to make a minimal project to reproduce this error for you.

I will always recommend tagging TArray’s with UPROPERTY. For example, if you have a TArray of references or pointers to actors, and those actors get destroyed, how are you going to validate the pointers or references you have? It is essentially pointing at garbage, not even null. Unless you are managing the lifetime of something manually, tag it as UPROPERTY so that the engine can handle it. Also use if(IsValid()) to check on the validity of stuff, it might be pending kill or something. (If something is destroyed and gets garbage collected, the TArray referencing/containing it will, if its tagged with UPROPERTY, change the reference to null so make sure to check IsValid)

That will very helpful. I appreciate the effort.

It was a TArray with references to custom classes which weren’t even UObjects. I just made them UObjects later. And it also wasn’t a TArray before, it was just a pure C++ array allocated with “new”. So initially I actually had a reason not to tag it with UPROPERTY. I was managing the lifetime of these objects. I used TArray later as a tool only, not because I needed UE4 to manage or be aware of something inside the array.

Are they references or actual instances of objects you are saving to the array? How are you initializing them? What they were before is irrelevant need to look at current setup and why it breaks. Just telling you my experience, as a general rule of thumb I always tag my TArrays with UPROPERTY, especially if they are pointing to objects that are not members/the object doesn’t directly “own”. Helps with validity checks too.

OK, I was able to reproduce it in a minimal project. How do I get it to you?

Oh, I didn’t see I could attach files here.

link text

You can either upload it here if it is small enough, upload it to dropbox or another third party site and link it here, or lastly send that same link to me in a private message on the forums, my forum username is matthew_clark

The fact of a TArray that isn’t attached to a UProperty being garbage collected isn’t a bug, as this is how garbage collection is intended to work. It is also not a bug that the pointers are set to addresses in memory after the objects they were pointing to are collected, as garbage collection knows nothing about the array itself and cannot reset these pointers to null. As BaderThanBad has suggested, you should use UProperty for anything that you do not want to be garbage collected as it could be picked up at any time.

Sorry. But this didn’t convince me. This actually sounds like an excuse to me and it doesn’t even make sense when considering the purpose of the UPROPERTY macro from the documentation.

Making a property show up in the editor
We have our class created, so now let’s create some properties that can be set by designers in the Unreal Editor. Exposing a property to the editor is quite easy using our special macro, UPROPERTY(). All you have to do is use the UPROPERTY(EditAnywhere) macro before your property declaration as seen in the class below.

So clearly the purpose of the UPROPERTY is to expose things to the UE4 Editor for them to be visible and/or editable, so what does this have to do with the Garbage Collector?

a) If you tell me that the GC only works on stuff that is exposed to the UE4 Editor, then we have a bug, because it shouldn’t touch stuff that isn’t exposed.

b) If you tell me that the GC is also active on stuff that isn’t exposed, then we have another bug, because the GC is clearly not working properly with things that aren’t exposed with a UPROPERTY.

c) If you tell me that the documentation is wrong and that the purpose of the UPROPERTY is also to expose things to the GC, then we have a bug again, because the GC is touching things that weren’t exposed to the GC.

So you can’t just tell me to hammer UPROPERTY’s into everything inside my classes just because “it works” or otherwise the GC goes berserk. If this is the case, then why have UPROPERTY at all? Just make the UHT consider every member to be a UPROPERTY for the sake of the GC and people will just add UPROPERTY when they really need to expose stuff to the Editor with special specifiers.

Either way, this is a bug or a very bad design flaw, because the GC is clearly violating any possible purpose you could give to the UPROPERTY macro.

I apologize for not being more detailed. UProperties are meant to expose the code to the editor itself for reflection, the facts of being able to add ‘EditAnywhere’ or ‘VisibleAnywhere’ are additional features that are not needed for reflection data to be generated, none of these specifiers are actually required. The garbage collector itself can see everything that is in the code. The problem is that anything that isn’t being reflected in the editor through UProperties is completely foreign to the garbage collector. More information about reflection data at the following link:

When the garbage collector doesn’t know that something is being used, or when you set a reflected property to be destroyed, the garbage collector gets rid of it as it sees these things as useless. More information about the garbage collector itself can be found at this link:

Non-UProperty variables still have their uses. They can for use inside of loops and functions, places where garbage collection will not occur and they will be used temporarily. Anything that is planned to be used long-term however, such as your array, should be a UProperty to ensure that it isn’t garbage collected.

I hope this helps.

Matthew Clark

Matthew, I love you for posting this documentation.

I thought that anything inside a UCLASS would automatically be reflected and visible by the GC. I thought that the UPROPERTY was only necessary to expose certain members to the editor, so it shouldn’t affect the workings of the GC. This made all the difference! With this information I now realize that the TArray is actually never touched by the GC. What happens is that my objects are just being deleted because I used NewObject() to create them in the first place. I didn’t have that problem before because I used new to create them instead of NewObject(). So now with them back to new, I now have an unreflected TArray pointing to objects the GC doesn’t even know about. And it works!

Lesson: The purpose of UPROPERTY is also reflection.

All UHT macros task (not just UPROPERTY) is to point UHT to register things to reflection system by generating extra code, everything in arguments are extra options. If UCLASS() did this job you would not able to use C++ features that are not supported by reflection system, but could be used in C++ only portion of the code.

I’m glad I could be of help. please be sure to let us know if you ever need anything else. The reflection documentation helped me a lot as well when I first started using the engine as I didn’t understand at all what UProperties were and why they needed to be used.

Have a nice day,

Matthew Clark