MemoryMinFreeVirtual
MemoryMinFreeVirtual
#Overview
name: MemoryMinFreeVirtual
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 MemoryMinFreeVirtual is to set a minimum threshold for available virtual memory during the cooking process in Unreal Engine 5. It is primarily used for memory management and garbage collection triggering in the cooking and asset generation systems.
This setting variable is mainly relied upon by the following Unreal Engine subsystems and modules:
- CookOnTheFlyServer in the UnrealEd module
- GenerateNaniteDisplacedMeshCommandlet in the NaniteDisplacedMeshEditor plugin
- 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 CookOnTheFlyServer and GenerateNaniteDisplacedMeshCommandlet implementations.
MemoryMinFreeVirtual often interacts with other memory-related variables such as MemoryMaxUsedVirtual, MemoryMinFreePhysical, and MemoryMaxUsedPhysical. These variables work together to manage memory usage during the cooking process.
Developers should be aware of the following when using this variable:
- The value is specified in megabytes (MB) in the configuration file but is converted to bytes when used in the code.
- It is used as a soft limit to trigger garbage collection when available virtual memory falls below this threshold.
- In some implementations, both MemoryMinFreeVirtual and MemoryMinFreePhysical need to be triggered to initiate garbage collection.
Best practices when using this variable include:
- Set an appropriate value based on your project’s requirements and the target platform’s capabilities.
- Consider the relationship between this variable and other memory-related settings to achieve optimal performance.
- Monitor memory usage during cooking and adjust the value if necessary to prevent out-of-memory issues while maintaining efficient resource utilization.
- Use in conjunction with other memory management tools and profiling techniques to ensure smooth cooking and asset generation processes.
#Setting Variables
#References In INI files
Location: <Workspace>/Engine/Config/BaseEditor.ini:355, 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:127
Scope (from outer to inner):
file
function int32 UGenerateNaniteDisplacedMeshCommandlet::Main
Source code excerpt:
// Loading the memory settings from the cook
int32 ValueInMB;
if (GConfig->GetInt(TEXT("CookSettings"), TEXT("MemoryMinFreeVirtual"), ValueInMB, GEditorIni))
{
ValueInMB = FMath::Max(ValueInMB, 0);
MemoryMinFreeVirtual = ValueInMB * 1024ULL * 1024ULL;
UE_LOG(LogNaniteDisplacedMesh, Display, TEXT("Loaded MemoryMinFreeVirtual from CookSettings (%d MiB)"), ValueInMB);
}
if (GConfig->GetInt(TEXT("CookSettings"), TEXT("MemoryMaxUsedVirtual"), ValueInMB, GEditorIni))
{
ValueInMB = FMath::Max(ValueInMB, 0);
#Loc: <Workspace>/Engine/Plugins/Experimental/NaniteDisplacedMesh/Source/NaniteDisplacedMeshEditor/Private/GenerateNaniteDisplacedMeshCommandlet.cpp:539
Scope (from outer to inner):
file
function void UGenerateNaniteDisplacedMeshCommandlet::LoadLevel
lambda-function
Source code excerpt:
{
const FPlatformMemoryStats MemoryStats = FPlatformMemory::GetStats();
if (MemoryMinFreeVirtual > 0 && MemoryStats.AvailableVirtual < MemoryMinFreeVirtual)
{
UE_LOG(LogNaniteDisplacedMesh, Display, TEXT("Low virtual memory available (%d MiB). kicking GC."), MemoryStats.AvailableVirtual / 1024 / 1024);
return true;
}
if (MemoryMinFreePhysical > 0 && MemoryStats.AvailablePhysical < MemoryMinFreePhysical)
#Loc: <Workspace>/Engine/Plugins/Experimental/NaniteDisplacedMesh/Source/NaniteDisplacedMeshEditor/Private/GenerateNaniteDisplacedMeshCommandlet.h:47
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
{
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Classes/CookOnTheSide/CookOnTheFlyServer.h:328
Scope (from outer to inner):
file
class class UCookOnTheFlyServer : public UObject, public FTickableEditorObject, public FExec, public UE::Cook::ICookInfo
Source code excerpt:
uint64 MemoryMaxUsedVirtual;
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 */
#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));
#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:6899
Scope (from outer to inner):
file
namespace UE::Cook
function void FInitializeConfigSettings::LoadLocal
Source code excerpt:
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))
{
UE_LOG(LogCook, Error, TEXT("Unrecognized value \"%s\" for MemoryTriggerGCAtPressureLevel. Expected None or Critical."),
#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:1066
Scope (from outer to inner):
file
namespace UE::Cook
function void FCookDirector::ActivateMachineResourceReduction
Source code excerpt:
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")
TEXT("\n\tMemoryMaxUsedPhysical %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:623
Scope (from outer to inner):
file
function FCbWriter& operator<<
Source code excerpt:
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;
Writer << "SoftGCDenominator" << Value.SoftGCDenominator;
Writer << "MinFreeUObjectIndicesBeforeGC" << Value.MinFreeUObjectIndicesBeforeGC;
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.cpp:649
Scope (from outer to inner):
file
function bool LoadFromCompactBinary
Source code excerpt:
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))
{
OutValue.MemoryTriggerGCAtPressureLevel = static_cast<FGenericPlatformMemoryStats::EMemoryPressureStatus>(PressureLevelAsInt);
}
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.cpp:686
Scope (from outer to inner):
file
namespace UE::Cook
function void FInitializeConfigSettings::MoveOrCopy
Source code excerpt:
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;
Target.SoftGCDenominator = Source.SoftGCDenominator;
Target.MinFreeUObjectIndicesBeforeGC = Source.MinFreeUObjectIndicesBeforeGC;
#Loc: <Workspace>/Engine/Source/Editor/UnrealEd/Private/Cooker/CookTypes.h:318
Scope (from outer to inner):
file
namespace UE::Cook
Source code excerpt:
uint64 MemoryMaxUsedVirtual;
uint64 MemoryMaxUsedPhysical;
uint64 MemoryMinFreeVirtual;
uint64 MemoryMinFreePhysical;
FGenericPlatformMemoryStats::EMemoryPressureStatus MemoryTriggerGCAtPressureLevel;
int32 MinFreeUObjectIndicesBeforeGC;
int32 MaxNumPackagesBeforePartialGC;
int32 SoftGCStartNumerator;
int32 SoftGCDenominator;