MemoryMaxUsedVirtual

MemoryMaxUsedVirtual

#Overview

name: MemoryMaxUsedVirtual

The value of this variable can be defined or overridden in .ini config files. 1 .ini config file referencing this setting variable.

It is referenced in 14 C++ source files.

#Summary

#Usage in the C++ source code

The purpose of MemoryMaxUsedVirtual is to set a maximum limit for virtual memory usage during the cooking process in Unreal Engine 5. It is primarily used as a threshold to trigger garbage collection and manage memory usage in the editor and cooking tools.

This setting variable is relied upon by the following Unreal Engine subsystems and modules:

  1. NaniteDisplacedMeshEditor plugin
  2. CookOnTheFlyServer in the UnrealEd module
  3. CookDirector in the UnrealEd module

The value of this variable is typically set in the GEditorIni configuration file under the [CookSettings] section. It can be loaded and modified programmatically, as seen in the UGenerateNaniteDisplacedMeshCommandlet and UCookOnTheFlyServer classes.

MemoryMaxUsedVirtual interacts with several other memory-related variables, such as:

  1. MemoryMaxUsedPhysical
  2. MemoryMinFreeVirtual
  3. MemoryMinFreePhysical
  4. MemoryTriggerGCAtPressureLevel

Developers must be aware of the following when using this variable:

  1. The value is specified in bytes, but it’s often set using megabytes (MB) and then converted to bytes in the code.
  2. Setting this value too low may cause frequent garbage collection, potentially slowing down the cooking process.
  3. Setting it too high may lead to excessive memory usage and potential system instability.

Best practices when using this variable include:

  1. Set a reasonable value based on the available system resources and the project’s requirements.
  2. Monitor memory usage during cooking to fine-tune this setting.
  3. Consider the interaction with other memory-related settings to achieve optimal performance and stability.
  4. Ensure that the value doesn’t exceed the total available physical memory on the system to prevent unintended behavior.
  5. Use in conjunction with other memory management settings for a comprehensive memory management strategy during cooking.

#Setting Variables

#References In INI files

Location: <Workspace>/Engine/Config/BaseEditor.ini:363, section: [CookSettings]

#References in C++ code

#Callsites

This variable is referenced in the following C++ source code:

#Loc: <Workspace>/Engine/Plugins/Experimental/NaniteDisplacedMesh/Source/NaniteDisplacedMeshEditor/Private/GenerateNaniteDisplacedMeshCommandlet.cpp:134

Scope (from outer to inner):

file
function     int32 UGenerateNaniteDisplacedMeshCommandlet::Main

Source code excerpt:

	}

	if (GConfig->GetInt(TEXT("CookSettings"), TEXT("MemoryMaxUsedVirtual"), ValueInMB, GEditorIni))
	{
		ValueInMB = FMath::Max(ValueInMB, 0);
		MemoryMaxUsedVirtual = ValueInMB * 1024ULL * 1024ULL;
		UE_LOG(LogNaniteDisplacedMesh, Display, TEXT("Loaded MemoryMaxUsedVirtual from CookSettings (%d MiB)"), ValueInMB);
	}

	if (GConfig->GetInt(TEXT("CookSettings"), TEXT("MemoryMinFreePhysical"), ValueInMB, GEditorIni))
	{
		ValueInMB = FMath::Max(ValueInMB, 0);

#Loc: <Workspace>/Engine/Plugins/Experimental/NaniteDisplacedMesh/Source/NaniteDisplacedMeshEditor/Private/GenerateNaniteDisplacedMeshCommandlet.cpp:551

Scope (from outer to inner):

file
function     void UGenerateNaniteDisplacedMeshCommandlet::LoadLevel
lambda-function

Source code excerpt:

			}

			if (MemoryMaxUsedVirtual > 0 && MemoryStats.UsedVirtual > MemoryMaxUsedVirtual)
			{
				UE_LOG(LogNaniteDisplacedMesh, Display, TEXT("High virtual memory usage (%d MiB). kicking GC."), MemoryStats.UsedVirtual / 1024 / 1024);
				return true;
			}

			if (MemoryMaxUsedPhysical > 0 && MemoryStats.UsedPhysical > MemoryMaxUsedPhysical)

#Loc: <Workspace>/Engine/Plugins/Experimental/NaniteDisplacedMesh/Source/NaniteDisplacedMeshEditor/Private/GenerateNaniteDisplacedMeshCommandlet.h:48

Scope (from outer to inner):

file
class        class UGenerateNaniteDisplacedMeshCommandlet : public UCommandlet

Source code excerpt:

	// Soft limit for when the garbage collector should be kicked (it use the same settings as the cook and will load these if found)
	uint64 MemoryMinFreeVirtual = 8192;
	uint64 MemoryMaxUsedVirtual= 98304;
	uint64 MemoryMinFreePhysical= 8192;
	uint64 MemoryMaxUsedPhysical= 0;

	struct FOnLinkDisplacedMeshArgs
	{
		FNaniteDisplacedMeshParams Parameters;

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Classes/CookOnTheSide/CookOnTheFlyServer.h:326

Scope (from outer to inner):

file
class        class UCookOnTheFlyServer : public UObject, public FTickableEditorObject, public FExec, public UE::Cook::ICookInfo

Source code excerpt:


	// Memory Limits for when to Collect Garbage
	uint64 MemoryMaxUsedVirtual;
	uint64 MemoryMaxUsedPhysical;
	uint64 MemoryMinFreeVirtual;
	uint64 MemoryMinFreePhysical;
	FGenericPlatformMemoryStats::EMemoryPressureStatus MemoryTriggerGCAtPressureLevel;
	float MemoryExpectedFreedToSpreadRatio;
	/** Max number of packages to save before we partial gc */

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/CookOnTheFlyServer.cpp:4976

Scope (from outer to inner):

file
function     bool UCookOnTheFlyServer::PumpHasExceededMaxMemory

Source code excerpt:

	// this can be useful if you demand that amount of memory as your min spec
	bool bMaxUsedTriggered = false;
	if (MemoryMaxUsedVirtual > 0 || MemoryMaxUsedPhysical > 0)
	{
		// check validity of trigger :
		// if the MaxUsed config exceeds the system memory, it can never be triggered and will prevent any GC :
		uint64 MaxMaxUsed = FMath::Max(MemoryMaxUsedVirtual,MemoryMaxUsedPhysical);
		if (MaxMaxUsed >= MemStats.TotalPhysical)
		{
			UE_CALL_ONCE([&]() {
				UE_LOG(LogCook, Warning, TEXT("Warning MemoryMaxUsed condition is larger than total memory (%dMiB >= %dMiB).  System does not have enough memory to cook this project."),
				static_cast<uint32>(MaxMaxUsed / 1024 / 1024), static_cast<uint32>(MemStats.TotalPhysical / 1024 / 1024));
				});
		}

		if (MemoryMaxUsedVirtual > 0 && MemStats.UsedVirtual >= MemoryMaxUsedVirtual)
		{
			TriggerMessages.Appendf(TEXT("\n  CookSettings.MemoryMaxUsedVirtual: Used virtual memory %dMiB is greater than %dMiB."),
				static_cast<uint32>(MemStats.UsedVirtual / 1024 / 1024), static_cast<uint32>(MemoryMaxUsedVirtual / 1024 / 1024));
			bMaxUsedTriggered = true;
		}
		if (MemoryMaxUsedPhysical > 0 && MemStats.UsedPhysical >= MemoryMaxUsedPhysical)
		{
			TriggerMessages.Appendf(TEXT("\n  CookSettings.MemoryMaxUsedPhysical: Used physical memory %dMiB is greater than %dMiB."),
				static_cast<uint32>(MemStats.UsedPhysical / 1024 / 1024), static_cast<uint32>(MemoryMaxUsedPhysical / 1024 / 1024));

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/CookOnTheFlyServer.cpp:5034

Scope (from outer to inner):

file
function     bool UCookOnTheFlyServer::PumpHasExceededMaxMemory

Source code excerpt:

		if (!bOnlyTriggerIfBothMinFreeAndMaxUsedTrigger ||
			((bMinFreeTriggered || (MemoryMinFreeVirtual <= 0 && MemoryMinFreePhysical <= 0)) &&
				(bMaxUsedTriggered || (MemoryMaxUsedVirtual <= 0 && MemoryMaxUsedPhysical <= 0))))
		{
			bTriggerGC = true;
		}
	}
	if (bPressureTriggered)
	{

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/CookOnTheFlyServer.cpp:6897

Scope (from outer to inner):

file
namespace    UE::Cook
function     void FInitializeConfigSettings::LoadLocal

Source code excerpt:

		return false;
	};
	MemoryMaxUsedVirtual = 0;
	MemoryMaxUsedPhysical = 0;
	MemoryMinFreeVirtual = 0;
	MemoryMinFreePhysical = 0;
	bUseSoftGC = false;
	SoftGCStartNumerator = 5;
	SoftGCDenominator = 10;

	ReadMemorySetting(TEXT("MemoryMaxUsedVirtual"), MemoryMaxUsedVirtual);
	ReadMemorySetting(TEXT("MemoryMaxUsedPhysical"), MemoryMaxUsedPhysical);
	ReadMemorySetting(TEXT("MemoryMinFreeVirtual"), MemoryMinFreeVirtual);
	ReadMemorySetting(TEXT("MemoryMinFreePhysical"), MemoryMinFreePhysical);
	FString ConfigText(TEXT("None"));
	GConfig->GetString(TEXT("CookSettings"), TEXT("MemoryTriggerGCAtPressureLevel"), ConfigText, GEditorIni);
	if (!LexTryParseString(MemoryTriggerGCAtPressureLevel, ConfigText))

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/CookOnTheFlyServer.cpp:6940

Scope (from outer to inner):

file
namespace    UE::Cook
function     void FInitializeConfigSettings::LoadLocal

Source code excerpt:

		TEXT("\n\tMemoryTriggerGCAtPressureLevel %s")
		TEXT("\n\tUseSoftGC %s%s"),
		MemoryMaxUsedVirtual / 1024 / 1024, MemoryMaxUsedPhysical / 1024 / 1024, MemoryMinFreeVirtual / 1024 / 1024, MemoryMinFreePhysical / 1024 / 1024,
		*LexToString(MemoryTriggerGCAtPressureLevel),
		bUseSoftGC ? TEXT("true") : TEXT("false"),
		bUseSoftGC ? *FString::Printf(TEXT(" (%d/%d)"), SoftGCStartNumerator, SoftGCDenominator) : TEXT(""));

	const FConfigSection* CacheSettings = GConfig->GetSection(TEXT("CookPlatformDataCacheSettings"), false, GEditorIni);
	if (CacheSettings)

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookDirector.cpp:1065

Scope (from outer to inner):

file
namespace    UE::Cook
function     void FCookDirector::ActivateMachineResourceReduction

Source code excerpt:

	// When running a multiprocess cook, we remove the Memory triggers and trigger GC based solely on PressureLevel. But keep the Soft GC settings
	COTFS.MemoryMaxUsedPhysical = 0;
	COTFS.MemoryMaxUsedVirtual = 0;
	COTFS.MemoryMinFreeVirtual = 0;
	COTFS.MemoryMinFreePhysical = 0;
	COTFS.MemoryTriggerGCAtPressureLevel = FGenericPlatformMemoryStats::EMemoryPressureStatus::Critical;

	UE_LOG(LogCook, Display, TEXT("CookMultiprocess changed CookSettings for Memory:")
		TEXT("\n\tMemoryMaxUsedVirtual %dMiB")

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookDirector.cpp:1077

Scope (from outer to inner):

file
namespace    UE::Cook
function     void FCookDirector::ActivateMachineResourceReduction

Source code excerpt:

		TEXT("\n\tMemoryTriggerGCAtPressureLevel %s")
		TEXT("\n\tUseSoftGC %s%s"),
		COTFS.MemoryMaxUsedVirtual / 1024 / 1024, COTFS.MemoryMaxUsedPhysical / 1024 / 1024,
		COTFS.MemoryMinFreeVirtual / 1024 / 1024, COTFS.MemoryMinFreePhysical / 1024 / 1024,
		*LexToString(COTFS.MemoryTriggerGCAtPressureLevel),
		COTFS.bUseSoftGC ? TEXT("true") : TEXT("false"),
		COTFS.bUseSoftGC ? *FString::Printf(TEXT(" (%d/%d)"), COTFS.SoftGCStartNumerator, COTFS.SoftGCDenominator) : TEXT("")
	);

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.cpp:621

Scope (from outer to inner):

file
function     FCbWriter& operator<<

Source code excerpt:

	Writer << "MemoryExpectedFreedToSpreadRatio" << Value.MemoryExpectedFreedToSpreadRatio;
	Writer << "IdleTimeToGC" << Value.IdleTimeToGC;
	Writer << "MemoryMaxUsedVirtual" << Value.MemoryMaxUsedVirtual;
	Writer << "MemoryMaxUsedPhysical" << Value.MemoryMaxUsedPhysical;
	Writer << "MemoryMinFreeVirtual" << Value.MemoryMinFreeVirtual;
	Writer << "MemoryMinFreePhysical" << Value.MemoryMinFreePhysical;
	Writer << "MemoryTriggerGCAtPressureLevel" << static_cast<uint8>(Value.MemoryTriggerGCAtPressureLevel);
	Writer << "bUseSoftGC" << Value.bUseSoftGC;
	Writer << "SoftGCStartNumerator" << Value.SoftGCStartNumerator;

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.cpp:647

Scope (from outer to inner):

file
function     bool LoadFromCompactBinary

Source code excerpt:

	bOk = LoadFromCompactBinary(Field["MemoryExpectedFreedToSpreadRatio"], OutValue.MemoryExpectedFreedToSpreadRatio) & bOk;
	bOk = LoadFromCompactBinary(Field["IdleTimeToGC"], OutValue.IdleTimeToGC) & bOk;
	bOk = LoadFromCompactBinary(Field["MemoryMaxUsedVirtual"], OutValue.MemoryMaxUsedVirtual) & bOk;
	bOk = LoadFromCompactBinary(Field["MemoryMaxUsedPhysical"], OutValue.MemoryMaxUsedPhysical) & bOk;
	bOk = LoadFromCompactBinary(Field["MemoryMinFreeVirtual"], OutValue.MemoryMinFreeVirtual) & bOk;
	bOk = LoadFromCompactBinary(Field["MemoryMinFreePhysical"], OutValue.MemoryMinFreePhysical) & bOk;
	uint8 PressureLevelAsInt;
	if (LoadFromCompactBinary(Field["MemoryTriggerGCAtPressureLevel"], PressureLevelAsInt))
	{

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.cpp:684

Scope (from outer to inner):

file
namespace    UE::Cook
function     void FInitializeConfigSettings::MoveOrCopy

Source code excerpt:

	Target.MemoryExpectedFreedToSpreadRatio = Source.MemoryExpectedFreedToSpreadRatio;
	Target.IdleTimeToGC = Source.IdleTimeToGC;
	Target.MemoryMaxUsedVirtual = Source.MemoryMaxUsedVirtual;
	Target.MemoryMaxUsedPhysical = Source.MemoryMaxUsedPhysical;
	Target.MemoryMinFreeVirtual = Source.MemoryMinFreeVirtual;
	Target.MemoryMinFreePhysical = Source.MemoryMinFreePhysical;
	Target.MemoryTriggerGCAtPressureLevel = Source.MemoryTriggerGCAtPressureLevel;
	Target.bUseSoftGC = Source.bUseSoftGC;
	Target.SoftGCStartNumerator = Source.SoftGCStartNumerator;

#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.h:316

Scope (from outer to inner):

file
namespace    UE::Cook

Source code excerpt:

		float MemoryExpectedFreedToSpreadRatio = 0.f;
		double IdleTimeToGC = 0.;
		uint64 MemoryMaxUsedVirtual;
		uint64 MemoryMaxUsedPhysical;
		uint64 MemoryMinFreeVirtual;
		uint64 MemoryMinFreePhysical;
		FGenericPlatformMemoryStats::EMemoryPressureStatus MemoryTriggerGCAtPressureLevel;
		int32 MinFreeUObjectIndicesBeforeGC;
		int32 MaxNumPackagesBeforePartialGC;