title: In Unreal C++ why are structs added to a TArray in an async thread not removed from RAM?
body: <p>I am asking this question from an Unreal Engine C++ code point of view but I am wondering if my problem is more to do with the nuances of C++'s way of operating.</p>
  <p>I have a Unreal actor. A simple class that holds an array of my own structs and runs a timer which triggers my own function. This function passes a reference of the actors array to an asynchronous task.</p>
  <p>This async task then goes to work, first creating a new struct, then adding two floats to its own internal TArray of floats and then adds that struct to the main actors array.</p>
  <p><strong>The problem:</strong></p>
  <p>After the async task has completed and I delete the actor from the level editor window, the system RAM is decreased as I call the Empty() function on the main actors array in the Destroyed() function but the RAM used by all of the structs (ie: The float array inside each struct) is left in memory and never cleared out.</p>
  <p><strong>Observations:</strong></p>
  <ol>
  <li>If I do not use an async task and run the same function inside the main thread ALL of the memory is cleared successfully.</li>
  <li>If I do not create the struct inside the async task and instead initalize the array with a load of structs which in turn are initialized with N number of floats inside the main thread, then pass that as a reference to the async task which works on the data, then the memory is also cleared out successfully.</li>
  </ol>
  <p><strong>What I would like to happen</strong></p>
  <p>I would like to pass a reference of the main actors array of structs to the async task. The async task would then go to work creating the data. Once it is complete, the main actor would then have access to the data and when the actor is deleted in the level editor window, ALL of the memory would be freed.</p>
  <p><strong>The code:</strong></p>
  <p><em>The definition of the data struct I am using:</em></p>
  <pre><code>struct FMyDataStruct
  {
      TArray&lt;float&gt; ArrayOfFloats;
       
      FMyDataStruct()
      {
          ArrayOfFloats.Empty();
          ArrayOfFloats.Shrink();
      }
  
      FMyDataStruct(int32 FloatCount)
      {
          ArrayOfFloats.Init(0.f, FloatCount);
      }
  
      ~FMyDataStruct()
      {
          ArrayOfFloats.Empty();
          ArrayOfFloats.Shrink();
      }
  };
  </code></pre>
  <p><em>The main actors definition of the array I am using:</em></p>
  <pre><code>TArray&lt;FMyDataStruct&gt; MyMainArray;
  </code></pre>
  <p><em>The main actors custom function I am running:</em></p>
  <pre><code>//CODE 1: This part DOES empty the RAM when run (ie: Run on main thread)
  /*for (int32 Index = 0; Index &lt; 50000000; Index++)
  {
      FMyDataStruct MyDataStruct;
      MyDataStruct.ArrayOfFloats.Add(FMath::Rand());
      MyDataStruct.ArrayOfFloats.Add(FMath::Rand());
      MyMainArray.Add(MyDataStruct);
  }*/
   
  //CODE 2: This does NOT empty the RAM when run. The two floats * 50,000,000 are left in system memory after the actor is deleted.
  auto Result = Async(EAsyncExecution::Thread, [&amp;]()
  {
      for (int32 Index = 0; Index &lt; 50000000; Index++)
      {
          FMyDataStruct MyDataStruct;
          MyDataStruct.ArrayOfFloats.Add(FMath::Rand());
          MyDataStruct.ArrayOfFloats.Add(FMath::Rand());
          MyMainArray.Add(MyDataStruct);
      }
  });
  </code></pre>
  <p><em>An example of initializing the array in the main thread, then working on it inside the async task:</em></p>
  <pre><code>//Initialize the array and its structs (plus the float array inside the struct)
  MyMainArray.Init(FMyDataStruct(2), 50000000);
  
  //TFuture/Async task
  auto Result = Async(EAsyncExecution::Thread, [Self]()
  {
      for (int32 Index = 0; Index &lt; 50000000; Index++)
      {
          Self-&gt;MyMainArray[Index].ArrayOfFloats[0] = FMath::Rand();
          Self-&gt;MyMainArray[Index].ArrayOfFloats[1] = FMath::Rand();
      }
  
      //Call the main threads task completed function
      AsyncTask(ENamedThreads::GameThread, [Self]()
      {
          if (Self != nullptr)
          {
              Self-&gt;MyTaskComplete();
          }
      });
  });
  </code></pre>
  <p><strong>Final thoughts:</strong></p>
  <p>Ultimately what I am asking is can anyone explain to me why from a C++ point of view the structs and their data would be removed from memory successfully when created/added from the main thread but then not removed from memory if created inside the async task/thread?</p>
  <p><strong>Update #1:</strong></p>
  <p><em>Here is a minimum reproducible example:</em></p>
  <ol>
  <li>Create a new project in either Unreal Engine 4.23, 4.24 or 4.25.</li>
  <li>Add a new C++ actor to the project and name it &quot;MyActor&quot;.</li>
  <li>Edit the source with the following:</li>
  </ol>
  <p><em>MyActor.h</em></p>
  <pre><code>#pragma once
  
  #include &quot;CoreMinimal.h&quot;
  #include &quot;GameFramework/Actor.h&quot;
  #include &quot;MyActor.generated.h&quot;
  
  struct FMyDataStruct
  {
      FMyDataStruct()
      {
          //Default Constructor
      }
  
      FMyDataStruct(const FMyDataStruct&amp; other)
          : ArrayOfFloats(other.ArrayOfFloats)
      {
          //Copy constructor
      }
  
      FMyDataStruct(FMyDataStruct&amp;&amp; other)
      {
          //Move constructor
          if (this != &amp;other)
          {
              ArrayOfFloats = MoveTemp(other.ArrayOfFloats);
          }
      }
  
      FMyDataStruct&amp; operator=(const FMyDataStruct&amp; other)
      {
          //Copy assignment operator
          if (this != &amp;other) //avoid self assignment
          {
              ArrayOfFloats = other.ArrayOfFloats; //UE4 TArray deep copy
          }
  
          return *this;
      }
  
      FMyDataStruct&amp; operator=(FMyDataStruct&amp;&amp; other)
      {
          //Move assignment operator
          if (this != &amp;other) //avoid self assignment
          {
              ArrayOfFloats = MoveTemp(other.ArrayOfFloats);
          }
  
          return *this;
      }
  
      FMyDataStruct(int32 FloatCount)
      {
          //Custom constructor to initialize the float array
          if (FloatCount &gt; 0)
          {
              ArrayOfFloats.Init(0.f, FloatCount);
          }
      }
  
      ~FMyDataStruct()
      {
          //Destructor
          ArrayOfFloats.Empty();
          ArrayOfFloats.Shrink();
      }
  
  public:
      TArray&lt;float&gt; ArrayOfFloats;
  };
  
  UCLASS()
  class BASICPROJECT1_API AMyActor : public AActor
  {
      GENERATED_BODY()
      
  public: 
      AMyActor();
  
  protected:
      virtual void Destroyed() override;
  
  public: 
      bool IsEditorOnly() const override;
      bool ShouldTickIfViewportsOnly() const override;
      virtual void Tick(float DeltaTime) override;
  
      void DoSomething();
      void AsyncTaskComplete();
  
      bool bShouldCount = true;
      float TimeCounter = 0.f;
      TArray&lt;FMyDataStruct&gt; MyMainArray;
  };
  </code></pre>
  <p><em>MyActor.cpp</em></p>
  <pre><code>#include &quot;MyActor.h&quot;
  
  AMyActor::AMyActor()
  {
      PrimaryActorTick.bCanEverTick = true;
  }
  
  void AMyActor::Tick(float DeltaTime)
  {
      if (!HasAnyFlags(RF_ClassDefaultObject)) //Check for not CDO. We only want to run in the instance
      {
          if (bShouldCount)
          {
              TimeCounter += DeltaTime;
  
              if (TimeCounter &gt;= 5.f)
              {
                  bShouldCount = false;
                  DoSomething();
              }
          }
      }
  }
  
  void AMyActor::Destroyed()
  {
      Super::Destroyed();
  
      MyMainArray.Empty();
      MyMainArray.Shrink();
  
      UE_LOG(LogTemp, Warning, TEXT(&quot;Actor got Destroyed!&quot;));
  }
  
  bool AMyActor::IsEditorOnly() const
  {
      return true;
  }
  
  bool AMyActor::ShouldTickIfViewportsOnly() const
  {
      return true;
  }
  
  void AMyActor::DoSomething()
  {
      //Change the code that is run:
      //1 = Main thread only
      //2 = Async only
      //3 = Init on main thread and process in async task
      //======================
      int32 CODE_SAMPLE = 1;
  
      UE_LOG(LogTemp, Warning, TEXT(&quot;Actor is running DoSomething()&quot;));
  
      TWeakObjectPtr&lt;AMyActor&gt; Self = this;
  
  
      if (CODE_SAMPLE == 1)
      {
          //CODE 1: Run on main thread. This part DOES empty the RAM when run. BLOCKS the editor window.
          //=========================================================================
          MyMainArray.Empty();
          MyMainArray.Shrink();
          MyMainArray.Reserve(50000000);
  
          for (int32 Index = 0; Index &lt; 50000000; Index++)
          {
              FMyDataStruct MyDataStruct;
              MyDataStruct.ArrayOfFloats.Reserve(2);
              MyDataStruct.ArrayOfFloats.Emplace(FMath::Rand());
              MyDataStruct.ArrayOfFloats.Emplace(FMath::Rand());
              
              MyMainArray.Emplace(MyDataStruct);
          }
  
          UE_LOG(LogTemp, Warning, TEXT(&quot;Main thread array fill is complete!&quot;));
      }
      else if (CODE_SAMPLE == 2)
      {
          //CODE 2: Run on async task. This does NOT empty the RAM when run
          //(4 bytes per float * 2 floats * 50,000,000 structs = 400Mb is left in system memory after the actor is deleted)
          //=========================================================================
          auto Result = Async(EAsyncExecution::Thread, [Self]()
              {
                  if (Self != nullptr)
                  {
                      Self-&gt;MyMainArray.Empty();
                      Self-&gt;MyMainArray.Shrink();
                      Self-&gt;MyMainArray.Reserve(50000000);
  
                      for (int32 Index = 0; Index &lt; 50000000; Index++)
                      {
                          FMyDataStruct MyDataStruct;
                          MyDataStruct.ArrayOfFloats.Reserve(2);
                          MyDataStruct.ArrayOfFloats.Emplace(FMath::Rand());
                          MyDataStruct.ArrayOfFloats.Emplace(FMath::Rand());
  
                          Self-&gt;MyMainArray.Emplace(MyDataStruct);
                      }
  
                      AsyncTask(ENamedThreads::GameThread, [Self]()
                          {
                              if (Self != nullptr)
                              {
                                  Self-&gt;AsyncTaskComplete();
                              }
                          });
                  }
              });
      }
      else if (CODE_SAMPLE == 3)
      {
          //CODE 3: Initialize the array in the main thread and work on the data in the async task
          //=========================================================================
          MyMainArray.Init(FMyDataStruct(2), 50000000);
  
          auto Result = Async(EAsyncExecution::Thread, [Self]()
              {
                  if (Self != nullptr)
                  {
                      for (int32 Index = 0; Index &lt; 50000000; Index++)
                      {
                          Self-&gt;MyMainArray[Index].ArrayOfFloats[0] = FMath::Rand();
                          Self-&gt;MyMainArray[Index].ArrayOfFloats[1] = FMath::Rand();
                      }
  
                      AsyncTask(ENamedThreads::GameThread, [Self]()
                          {
                              if (Self != nullptr)
                              {
                                  Self-&gt;AsyncTaskComplete();
                              }
                          });
                  }
              });
      }
  }
  
  void AMyActor::AsyncTaskComplete()
  {
      UE_LOG(LogTemp, Warning, TEXT(&quot;Async task is complete!&quot;));
  }
  </code></pre>
  <ol start="4">
  <li>Compile and run the project.</li>
  <li>Drag the actor into the level editor window.</li>
  <li>After 5 seconds the code will run and the RAM usage will increase to 1750Mb.</li>
  <li>Select the actor in the outliner window and delete it.</li>
  <li>The RAM usage will perform like this:</li>
  </ol>
  <p><em><strong>CODE 1:</strong> RAM is cleared out all the way to the starting RAM usage of 650Mb.</em></p>
  <p><em><strong>CODE 2:</strong> RAM is cleared down to 1000Mb and never returns to starting usage.</em></p>
  <p><em><strong>CODE 3:</strong> RAM is cleared out all the way to the starting RAM usage of 650Mb.</em></p>
  <p>I thank you for your help.</p>