MemoryMinFreePhysical
MemoryMinFreePhysical
#Overview
name: MemoryMinFreePhysical
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 15
C++ source files.
#Summary
#Usage in the C++ source code
The purpose of MemoryMinFreePhysical is to set a threshold for the minimum amount of free physical memory that should be available during certain operations in Unreal Engine 5. It is primarily used for memory management and performance optimization.
This setting variable is utilized by several Unreal Engine subsystems and modules:
- The Nanite Displaced Mesh Editor plugin
- The Cook On The Fly Server
- The Cook Director
- World Partition Helpers
The value of MemoryMinFreePhysical is typically set in the configuration files, specifically in the GEditorIni file under the “CookSettings” section. It can be loaded and modified programmatically as well.
MemoryMinFreePhysical often interacts with other memory-related variables such as MemoryMaxUsedPhysical, MemoryMinFreeVirtual, and MemoryMaxUsedVirtual. These variables work together to manage memory usage and trigger garbage collection when necessary.
Developers should be aware of the following when using this variable:
- The value is typically expressed in bytes, but it’s often set in megabytes and then converted to bytes in the code.
- It’s used as a threshold to trigger garbage collection or other memory management operations when available physical memory falls below this value.
- In some cases, it’s used in conjunction with MemoryMaxUsedPhysical to determine when to perform memory management tasks.
Best practices when using this variable include:
- Set an appropriate value based on the target hardware specifications and the game’s memory requirements.
- Consider the relationship between this variable and other memory-related settings to ensure optimal performance.
- Monitor memory usage during development and adjust the value as needed to balance performance and stability.
- Be cautious when modifying this value, as setting it too low may result in frequent garbage collection, while setting it too high may lead to unnecessary memory restrictions.
#Setting Variables
#References In INI files
Location: <Workspace>/Engine/Config/BaseEditor.ini:354, section: [CookSettings]
- INI Section:
CookSettings
- Raw value:
2048
- Is Array:
False
#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:141
Scope (from outer to inner):
file
function int32 UGenerateNaniteDisplacedMeshCommandlet::Main
Source code excerpt:
}
if (GConfig->GetInt(TEXT("CookSettings"), TEXT("MemoryMinFreePhysical"), ValueInMB, GEditorIni))
{
ValueInMB = FMath::Max(ValueInMB, 0);
MemoryMinFreePhysical = ValueInMB * 1024ULL * 1024ULL;
UE_LOG(LogNaniteDisplacedMesh, Display, TEXT("Loaded MemoryMinFreePhysical from CookSettings (%d MiB)"), ValueInMB);
}
if (GConfig->GetInt(TEXT("CookSettings"), TEXT("MemoryMaxUsedPhysical"), ValueInMB, GEditorIni))
{
ValueInMB = FMath::Max(ValueInMB, 0);
#Loc: <Workspace>/Engine/Plugins/Experimental/NaniteDisplacedMesh/Source/NaniteDisplacedMeshEditor/Private/GenerateNaniteDisplacedMeshCommandlet.cpp:545
Scope (from outer to inner):
file
function void UGenerateNaniteDisplacedMeshCommandlet::LoadLevel
lambda-function
Source code excerpt:
}
if (MemoryMinFreePhysical > 0 && MemoryStats.AvailablePhysical < MemoryMinFreePhysical)
{
UE_LOG(LogNaniteDisplacedMesh, Display, TEXT("Low physical memory available (%d MiB). kicking GC."), MemoryStats.AvailablePhysical / 1024 / 1024);
return true;
}
if (MemoryMaxUsedVirtual > 0 && MemoryStats.UsedVirtual > MemoryMaxUsedVirtual)
#Loc: <Workspace>/Engine/Plugins/Experimental/NaniteDisplacedMesh/Source/NaniteDisplacedMeshEditor/Private/GenerateNaniteDisplacedMeshCommandlet.h:49
Scope (from outer to inner):
file
class class UGenerateNaniteDisplacedMeshCommandlet : public UCommandlet
Source code excerpt:
uint64 MemoryMinFreeVirtual = 8192;
uint64 MemoryMaxUsedVirtual= 98304;
uint64 MemoryMinFreePhysical= 8192;
uint64 MemoryMaxUsedPhysical= 0;
struct FOnLinkDisplacedMeshArgs
{
FNaniteDisplacedMeshParams Parameters;
FString Folder;
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Classes/CookOnTheSide/CookOnTheFlyServer.h:329
Scope (from outer to inner):
file
class class UCookOnTheFlyServer : public UObject, public FTickableEditorObject, public FExec, public UE::Cook::ICookInfo
Source code excerpt:
uint64 MemoryMaxUsedPhysical;
uint64 MemoryMinFreeVirtual;
uint64 MemoryMinFreePhysical;
FGenericPlatformMemoryStats::EMemoryPressureStatus MemoryTriggerGCAtPressureLevel;
float MemoryExpectedFreedToSpreadRatio;
/** Max number of packages to save before we partial gc */
int32 MaxNumPackagesBeforePartialGC;
/** Max number of concurrent shader jobs reducing this too low will increase cook time */
int32 MaxConcurrentShaderJobs;
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/CookOnTheFlyServer.cpp:4950
Scope (from outer to inner):
file
function bool UCookOnTheFlyServer::PumpHasExceededMaxMemory
Source code excerpt:
bool bMinFreeTriggered = false;
if (MemoryMinFreeVirtual > 0 || MemoryMinFreePhysical > 0)
{
// trigger GC if we have less than MemoryMinFreeVirtual OR MemoryMinFreePhysical
// the check done in AssetCompilingManager is against the min of the two :
//uint64 AvailableMemory = FMath::Min(MemStats.AvailablePhysical, MemStats.AvailableVirtual);
// so for consistency the same check should be done here
// you can get that by setting the MemoryMinFreeVirtual and MemoryMinFreePhysical config to be the same
// AvailableVirtual is actually ullAvailPageFile (commit charge available)
if (MemoryMinFreeVirtual > 0 && MemStats.AvailableVirtual < MemoryMinFreeVirtual)
{
TriggerMessages.Appendf(TEXT("\n CookSettings.MemoryMinFreeVirtual: Available virtual memory %dMiB is less than %dMiB."),
static_cast<uint32>(MemStats.AvailableVirtual / 1024 / 1024), static_cast<uint32>(MemoryMinFreeVirtual / 1024 / 1024));
bMinFreeTriggered = true;
}
if (MemoryMinFreePhysical > 0 && MemStats.AvailablePhysical < MemoryMinFreePhysical)
{
TriggerMessages.Appendf(TEXT("\n CookSettings.MemoryMinFreePhysical: Available physical memory %dMiB is less than %dMiB."),
static_cast<uint32>(MemStats.AvailablePhysical / 1024 / 1024), static_cast<uint32>(MemoryMinFreePhysical / 1024 / 1024));
bMinFreeTriggered = true;
}
}
// if MemoryMaxUsed is set, we won't GC until at least that much mem is used
// this can be useful if you demand that amount of memory as your min spec
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/CookOnTheFlyServer.cpp:5033
Scope (from outer to inner):
file
function bool UCookOnTheFlyServer::PumpHasExceededMaxMemory
Source code excerpt:
const bool bOnlyTriggerIfBothMinFreeAndMaxUsedTrigger = true;
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:6900
Scope (from outer to inner):
file
namespace UE::Cook
function void FInitializeConfigSettings::LoadLocal
Source code excerpt:
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))
{
UE_LOG(LogCook, Error, TEXT("Unrecognized value \"%s\" for MemoryTriggerGCAtPressureLevel. Expected None or Critical."),
*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:1067
Scope (from outer to inner):
file
namespace UE::Cook
function void FCookDirector::ActivateMachineResourceReduction
Source code excerpt:
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")
TEXT("\n\tMemoryMaxUsedPhysical %dMiB")
TEXT("\n\tMemoryMinFreeVirtual %dMiB")
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookDirector.cpp:1078
Scope (from outer to inner):
file
namespace UE::Cook
function void FCookDirector::ActivateMachineResourceReduction
Source code excerpt:
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("")
);
// Set CoreLimit for updating workerthreads in this process and passing to the commandline for workers
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.cpp:624
Scope (from outer to inner):
file
function FCbWriter& operator<<
Source code excerpt:
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;
Writer << "SoftGCDenominator" << Value.SoftGCDenominator;
Writer << "MinFreeUObjectIndicesBeforeGC" << Value.MinFreeUObjectIndicesBeforeGC;
Writer << "MaxNumPackagesBeforePartialGC" << Value.MaxNumPackagesBeforePartialGC;
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.cpp:650
Scope (from outer to inner):
file
function bool LoadFromCompactBinary
Source code excerpt:
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))
{
OutValue.MemoryTriggerGCAtPressureLevel = static_cast<FGenericPlatformMemoryStats::EMemoryPressureStatus>(PressureLevelAsInt);
}
else
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.cpp:687
Scope (from outer to inner):
file
namespace UE::Cook
function void FInitializeConfigSettings::MoveOrCopy
Source code excerpt:
Target.MemoryMaxUsedPhysical = Source.MemoryMaxUsedPhysical;
Target.MemoryMinFreeVirtual = Source.MemoryMinFreeVirtual;
Target.MemoryMinFreePhysical = Source.MemoryMinFreePhysical;
Target.MemoryTriggerGCAtPressureLevel = Source.MemoryTriggerGCAtPressureLevel;
Target.bUseSoftGC = Source.bUseSoftGC;
Target.SoftGCStartNumerator = Source.SoftGCStartNumerator;
Target.SoftGCDenominator = Source.SoftGCDenominator;
Target.MinFreeUObjectIndicesBeforeGC = Source.MinFreeUObjectIndicesBeforeGC;
Target.MaxNumPackagesBeforePartialGC = Source.MaxNumPackagesBeforePartialGC;
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.h:319
Scope (from outer to inner):
file
namespace UE::Cook
Source code excerpt:
uint64 MemoryMaxUsedPhysical;
uint64 MemoryMinFreeVirtual;
uint64 MemoryMinFreePhysical;
FGenericPlatformMemoryStats::EMemoryPressureStatus MemoryTriggerGCAtPressureLevel;
int32 MinFreeUObjectIndicesBeforeGC;
int32 MaxNumPackagesBeforePartialGC;
int32 SoftGCStartNumerator;
int32 SoftGCDenominator;
TArray<FString> ConfigSettingDenyList;
#Loc: <Workspace>/Engine/Source/Runtime/Engine/Private/WorldPartition/WorldPartitionHelpers.cpp:238
Scope (from outer to inner):
file
function bool FWorldPartitionHelpers::HasExceededMaxMemory
Source code excerpt:
const FPlatformMemoryStats MemStats = FPlatformMemory::GetStats();
const uint64 MemoryMinFreePhysical = 1llu * 1024 * 1024 * 1024;
const uint64 MemoryMaxUsedPhysical = FMath::Max(32llu * 1024 * 1024 * 1024, MemStats.TotalPhysical / 2);
const bool bHasExceededMinFreePhysical = MemStats.AvailablePhysical < MemoryMinFreePhysical;
const bool bHasExceededMaxUsedPhysical = MemStats.UsedPhysical >= MemoryMaxUsedPhysical;
const bool bHasExceededMaxMemory = bHasExceededMinFreePhysical || bHasExceededMaxUsedPhysical;
return bHasExceededMaxMemory;
}