Description

The cause of this problem is the Engine has code paths which register a new allocated object to Map without deleting the previous object.

In the repro project, whenever hitting Space Bar, the level blueprint calls Create Level Sequence Player and it constructs ULevelSequenceBurnInOptions which has UCLASS(config=EditorPerProjectUserSettings) specifiers. But in the packaged environment, EditorPerProjectUserSettings.ini could not found. As the result, FConfigContext::PrepareForLoad() function registers empty FConfigFile entry like;

ConfigFile = &ConfigSystem->Add(DestIniFilename, FConfigFile()); 

Note that the Add function calls new operator inside the method. But this entry is Empty and Num() returns zero, so when calling PrepareForLoad for EditorPerProjectUserSettings.ini from the second time cannot hit memory cache;

if (bLookForExistingFile)
{
	// look up a file that already exists and matches the name
	FConfigFile* FoundConfigFile = ConfigSystem->KnownFiles.GetMutableFile(*BaseIniName);
	if (FoundConfigFile == nullptr)
	{
		FoundConfigFile = ConfigSystem->FindConfigFile(*DestIniFilename);
		//// @todo: this is test to see if we can simplify this to FindConfigFileWithBaseName always (if it never fires, we can)
		//check(FoundConfigFile == nullptr || FoundConfigFile == ConfigSystem->FindConfigFileWithBaseName(*BaseIniName))
	}
	if (FoundConfigFile != nullptr && FoundConfigFile->Num() > 0)
	{
		ConfigFile = FoundConfigFile;
		bPerformLoad = false;
		return true;
	}
} 

Note that FoundConfigFile is not nullptr but FoundConfigFile->Num() returns zero. Then this function add new empty entry with the same key without removing the existing entry;

// setup ConfigFile to read into if one isn't already set
if (ConfigFile == nullptr)
{
	// first look for a KnownFile
	ConfigFile = ConfigSystem->KnownFiles.GetMutableFile(*BaseIniName);
	if (ConfigFile == nullptr)
	{
		check(!DestIniFilename.IsEmpty());

		ConfigFile = &ConfigSystem->Add(DestIniFilename, FConfigFile());
	}
} 

The possible solution is to remove the existing entry just before calling Add or regard ConfigFile is found but pretend to do loading.

if (bLookForExistingFile)
{
    // ...skip...
    if (FoundConfigFile != nullptr && FoundConfigFile->Num() > 0)
    {
        ConfigFile = FoundConfigFile;
        bPerformLoad = false;
        return true;
    }
+   else if (FoundConfigFile != nullptr)
+   {
+       ConfigFile = FoundConfigFile;
+       bPerformLoad = true;
+       return true;
+   }
}  

This could be more refactored.

Steps to Reproduce

To reproduce this issue, need to set up the situation where some config ini file(s) cannot loaded and are not allowed to create.

  1. Create a project and a new level (set this level to default map)
  2. Add a level sequence.
  3. Open level blueprint and make node network so that pressing space bar is to call CreateLevelSequencePlayer and Destroy it (See below screenshot.)
  4. Build the Windows package so that EditorPerProjectUserSettings.ini cannot be found.
  5. Run the built execution file and hit space bar
  6. Monitor a memory leak by attaching debugger or memory insight.

I attached the repro project which can skip from step 1 to 3.

[Image Removed]

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-198500 in the post.

1
Login to Vote

Unresolved
ComponentUE - Foundation - Core
Affects Versions5.1.15.25.3.1
Target Fix5.5
CreatedOct 19, 2023
UpdatedFeb 29, 2024