Spawned actors then destroy() it, but the memory cannot be fully released

Hi everyone,

I did a simple test to released memory : Create a square mesh and spawn it for 1000 times, then destroy them all.

Here is the code to create square :



AGenerateMesh::AGenerateMesh()
{
  PrimaryActorTick.bCanEverTick = false;

  mesh = CreateDefaultSubobject<UProceduralMeshComponent>(TEXT("GeneratedMesh"));
  RootComponent = mesh;
}

void AGenerateMesh::BeginPlay()
{
  Super::BeginPlay();
  CreateSquare();
}

void AGenerateMesh::CreateSquare()
{
  TArray<FVector> vertices;
  vertices.Add(FVector(0, 0, 0));
  vertices.Add(FVector(0, 100, 0));
  vertices.Add(FVector(0, 0, 100));
  vertices.Add(FVector(0, 0, 100));
  vertices.Add(FVector(0, 100, 0));
  vertices.Add(FVector(0, 100, 100));

  TArray<int32> Triangles;
  Triangles.Add(0);
  Triangles.Add(1);
  Triangles.Add(2);
  Triangles.Add(3);
  Triangles.Add(4);
  Triangles.Add(5);

  TArray<FVector> normals;
  normals.Add(FVector(1, 0, 0));
  normals.Add(FVector(1, 0, 0));
  normals.Add(FVector(1, 0, 0));
  normals.Add(FVector(1, 0, 0));
  normals.Add(FVector(1, 0, 0));
  normals.Add(FVector(1, 0, 0));

  TArray<FVector2D> UV0;
  UV0.Add(FVector2D(0, 0));
  UV0.Add(FVector2D(10, 0));
  UV0.Add(FVector2D(0, 10));
  UV0.Add(FVector2D(0, 10));
  UV0.Add(FVector2D(10, 0));
  UV0.Add(FVector2D(10, 10));

  TArray<FProcMeshTangent> tangents;
  tangents.Add(FProcMeshTangent(0, 1, 0));
  tangents.Add(FProcMeshTangent(0, 1, 0));
  tangents.Add(FProcMeshTangent(0, 1, 0));
  tangents.Add(FProcMeshTangent(0, 1, 0));
  tangents.Add(FProcMeshTangent(0, 1, 0));
  tangents.Add(FProcMeshTangent(0, 1, 0));

  TArray<FLinearColor> vertexColors;
  vertexColors.Add(FLinearColor(0.75, 0.75, 0.75, 1.0));
  vertexColors.Add(FLinearColor(0.75, 0.75, 0.75, 1.0));
  vertexColors.Add(FLinearColor(0.75, 0.75, 0.75, 1.0));
  vertexColors.Add(FLinearColor(0.75, 0.75, 0.75, 1.0));
  vertexColors.Add(FLinearColor(0.75, 0.75, 0.75, 1.0));
  vertexColors.Add(FLinearColor(0.75, 0.75, 0.75, 1.0));

  mesh->CreateMeshSection_LinearColor(0, vertices, Triangles, normals, UV0, vertexColors, tangents, true);
}


here is the code to spawn square mesh



void ASpawnObject::SpawnObj()
{
  for (int i = 0; i < 1000; i++)
  {
    AActor* SpawnItem = GetWorld()->SpawnActor(AGenerateMesh::StaticClass(), &Pos, (0, 0, 0));
    SpawnedActorArray.Add(SpawnItem);
  }
}


here is the code to destroy square mesh



void ASpawnObject::DestroyObj()
{
  int ItemCnt = SpawnedActorArray.Num();
  for (int i = 0; i < ItemCnt; i++)
  {
    AActor* actor = SpawnedActorArray*;
    TArray<UActorComponent*> actorC;
    actor->GetComponents(actorC);

    for (auto aa : actorC)
    {
      UProceduralMeshComponent* bb = Cast<UProceduralMeshComponent>(aa);
      if (bb)
      {
        bb->ClearAllMeshSections();
      }
    }
    SpawnedActorArray*->RemoveFromRoot();
    SpawnedActorArray*->Destroy();
  }
  SpawnedActorArray.Empty();

  GEngine->ForceGarbageCollection();
}


void ASpawnObject::DestroyObj_GC()
{
  int ItemCnt = SpawnedActorArray.Num();
  for (int i = 0; i < ItemCnt; i++)
  {
    SpawnedActorArray*->MarkPendingKill();
    SpawnedActorArray*->ConditionalBeginDestroy();
    SpawnedActorArray* = nullptr;
  }
  SpawnedActorArray.Empty();

  GEngine->ForceGarbageCollection();
}


I tried 2 way to destroy them and the result is similar.

The memory statistics :
Before spawned : 1011mb,
After spawned:1234mb,
After destroy 10 min: 1090mb
There is 80 mb memory not be released and the wait is too long.

A01.JPG
Adjust the parameter in GC can improve it or not?
Is there any way to clear the memory?

Thanks everyone.

Can anyone advise me?

Thanks

Dive into ForceGarbageCollection() function.
I bet Epic made some changes to optimize internal memory pool and streaming for Fortnite, so there could be a lot of CDOs there that will never be released.

Sad…it seems no solution?

Try turning the cluster size down and unchecking allow incremental BeginDestroy?

It is very unlikely to see the exact number again, due to fragmentation of the allocator, various data structures being sized up to contain the new actors but not back down since that would be mostly pointless, and similar.

What you should find is that if you repeat this test many times (spawn -> destroy -> gc -> repeat) then it evens out and grows progressively slower / stops growing eventually. If that isn’t the case, you might have found a leak that requires more investigation.

Thanks for reply Natalo77, Zeblote

According to the test, it is basically similar to the situation you described, Zeblote.
spawn -> destroy -> gc -> repeat, and grows progressively slower.
And I guess it’s normal in UE, Thanks for your answer.