I have created an editor only actor that can be placed into the editor at design time. This actor holds a TArray of floats that is not marked with UPROPERTY. I add some test data to the array in the OnConstruction() function which is added successfully. The RAM usage goes up at this point because of all that data. If I then delete the actor from the level/editor then the memory is never freed.
What’s strange is that if I try to empty the array inside the actors destructor it says the array is already “empty” but yet the memory is still being used as reported by Windows task manager. This memory never reduces even after some time.
I have tried emptying the TArray in the destructor of the actor but when debugging in Visual Studio it states that the array is empty but the memory amount being used in Windows task manager still reports a significant amount of usage.
The memory before adding the actor in the editor to a level is around 600mb and after adding it, it is 1400mb. If I then delete the actor from the level the RAM stays at about 1400mb.
Hi! I try the snippet code you are using and have some ideas what is there. The thing is that the engine use Actor config as the base(aka Template) to create level. So Actor is constructed by this base(aka Template) for this Actor class. The items that were added in OnConstruction method are related to this base(aka template) for you actor. if you try to add some elements at runtime and end the game - you’ll see that they will be cleaned up, because they are not part of base.
I am struggling to understand the concept of ‘Template’. I added a check in the OnConstruction for
IsTemplate(RF_Transient) == false
but the MyStructArray is empty in the actors destructor even though the RAM is still in use.
If I change the code to
IsTemplate(RF_Transient) == true
then the MyStructArray is full of data and can be emptied in the actors destructor. This then empties the RAM usage.
What I am not understanding is: I thought IsTemplate(RF_Transient) == true means it IS the base/template version of the actor in which case I don’t want it running MyFunc in that case? I’m unsure. But if I set it to check for False then MyFunc is run in some other version of the actor in which case the MyStructArray can no longer be emptied in the actors destructor and therefore the RAM is never cleared out.
I am confused about why there are different versions of the actor and why the TArray is only available in certain ones…
If anyone can help me understand or direct me to a page that describes it then I would appreciate it.
It seems like Kehel18 said, when an actor is dragged into the level the CDO is created and its OnConstruction function is run. Then the instance of the actor is created and its OnConstruction function is run. To test for which version is which inside the OnConstruction function use:
If I run the MyFunc function inside the IF statement it adds the data to MyStructArray.
What I cannot figure out is the deletion/destruction side of the actor.
If you delete the actor in the level editor then the class/actors destructor is run immediately but the MyStructArray is empty for some reason.
If you then close the editor window it runs the destructor again (even though it no longer appears in the level editor) and this time the MyStructArray is populated with data and is therefore emptied and its RAM is released.
I just make a simple example proj to look at outers and flags and so on
OnConstruction can be run several times by object. It is running when opening level or placed actor in level. While destructor is called when level is unloaded. A template object is fully determined by having outer with flags in param.
Using the HasAnyFlags() distinguishes between the ghost/CDO and instance of my actor and if I implement the Destroyed() event I have been able to see TArrays being emptied there correctly with a release of memory.
I could do with your opinion on a slightly different angle to this, TArrays not releasing their memory.
It’s to do with a TArray that exists in the main actor but is filled inside a separate FRunnable thread. If you pass the array by reference to the thread using “FMyTask MyTask = new FMyTask(MyMainArray, etc…)” it fills the array correctly but when the actor is deleted and the Destroyed() function is run, the MyMainArray array shows that data is in there and using Empty() shows that the array is then empty when debugging in visual studio but the RAM is not released in that scenario.
By the way, do you log actor pointers in Fill routine and empty routine? Are they same? And one more - your code is in Tick function. Is your default object Ticking? And is this a special case for something?
I added some UE_LOG to the code and the array is filled in the task loop then when the task exits it reports zero items in the array and there is nothing in the main array in the actor but yet 600mb of ram is still in use
It seems that the reference is not correct inside the task and therefore does not change the array passed to it, just its own version of it some how.
The ram shoots up from a base line in the editor of 570mb in the editor with no actor to 2800mb with the tasks loop running. Then when the loop is done is goes down to 1400mb and sits there forever, even after the actor is deleted.
Yes, the running in tick function is for a building/updating facility that the actor will do.
I also added a HasAnyTags(RF_CreateDefaultObject) if statement check to disable the Tick if that is true.
In the callback function that I was using that the thread calls when it is done was setting the MyMainArray TArray to be a copy of the Results items, which that results code was not even in use inside the thread.
After that was removed the array now does not suddenly change to zero items when the task runs its callback function.
So the TArray is emptied but still says that the ram is sitting at 1300mb when it should be 600mb when the actor is deleted.
I tracked down this extra 700mb to be the one float I was adding to the test struct array I was adding inside the loop that fills the array in the first place inside the thread.
I was under the impression that running the Empty() function on a TArray would invoke the destructor of the struct for each element in the main TArray which would in turn empty its own float array as the code shows.
That does not seem to be the case as the 700mb from that one float value multiplied up by the number of structs is still in memory and never released.