diff --git a/src/common/system.cpp b/src/common/system.cpp index 9f049e84dd..e0e2ce6c3f 100644 --- a/src/common/system.cpp +++ b/src/common/system.cpp @@ -115,16 +115,21 @@ int64_t GetStartupTime() return nStartupTime; } +size_t g_low_memory_threshold = 10 * 1024 * 1024 /* 10 MB */; + bool SystemNeedsMemoryReleased() { - constexpr size_t low_memory_threshold = 10 * 1024 * 1024 /* 10 MB */; + if (g_low_memory_threshold <= 0) { + // Intentionally bypass other metrics when disabled entirely + return false; + } #ifdef WIN32 MEMORYSTATUSEX mem_status; mem_status.dwLength = sizeof(mem_status); if (GlobalMemoryStatusEx(&mem_status)) { if (mem_status.dwMemoryLoad >= 99 || - mem_status.ullAvailPhys < low_memory_threshold || - mem_status.ullAvailVirtual < low_memory_threshold) { + mem_status.ullAvailPhys < g_low_memory_threshold || + mem_status.ullAvailVirtual < g_low_memory_threshold) { LogPrintf("%s: YES: %s%% memory load; %s available physical memory; %s available virtual memory\n", __func__, int(mem_status.dwMemoryLoad), size_t(mem_status.ullAvailPhys), size_t(mem_status.ullAvailVirtual)); return true; } @@ -136,7 +141,7 @@ bool SystemNeedsMemoryReleased() // Explicitly 64-bit in case of 32-bit userspace on 64-bit kernel const uint64_t free_ram = uint64_t(sys_info.freeram) * sys_info.mem_unit; const uint64_t buffer_ram = uint64_t(sys_info.bufferram) * sys_info.mem_unit; - if (free_ram + buffer_ram < low_memory_threshold) { + if (free_ram + buffer_ram < g_low_memory_threshold) { LogPrintf("%s: YES: %s free RAM + %s buffer RAM\n", __func__, free_ram, buffer_ram); return true; } diff --git a/src/common/system.h b/src/common/system.h index 3a31e80cd3..0ef6a16462 100644 --- a/src/common/system.h +++ b/src/common/system.h @@ -25,6 +25,8 @@ std::string ShellEscape(const std::string& arg); void runCommand(const std::string& strCommand); #endif +extern size_t g_low_memory_threshold; + bool SystemNeedsMemoryReleased(); /** diff --git a/src/init.cpp b/src/init.cpp index 988daefeec..5b1a1df68f 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -482,6 +482,7 @@ void SetupServerArgs(ArgsManager& argsman) argsman.AddArg("-includeconf=", "Specify additional configuration file, relative to the -datadir path (only useable from configuration file, not command line)", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-allowignoredconf", strprintf("For backwards compatibility, treat an unused %s file in the datadir as a warning, not an error.", BITCOIN_CONF_FILENAME), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-loadblock=", "Imports blocks from external file on startup", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); + argsman.AddArg("-lowmem=", strprintf("If system available memory falls below MiB, flush caches (0 to disable, default: %s)", g_low_memory_threshold / 1024 / 1024), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-maxmempool=", strprintf("Keep the transaction memory pool below megabytes (default: %u)", DEFAULT_MAX_MEMPOOL_SIZE_MB), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-maxorphantx=", strprintf("Keep at most unconnectable transactions in memory (default: %u)", DEFAULT_MAX_ORPHAN_TRANSACTIONS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); argsman.AddArg("-mempoolexpiry=", strprintf("Do not keep transactions in the mempool longer than hours (default: %u)", DEFAULT_MEMPOOL_EXPIRY_HOURS), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS); @@ -1491,6 +1492,13 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info) } LogPrintf("* Using %.1f MiB for in-memory UTXO set (plus up to %.1f MiB of unused mempool space)\n", cache_sizes.coins * (1.0 / 1024 / 1024), mempool_opts.max_size_bytes * (1.0 / 1024 / 1024)); + if (gArgs.IsArgSet("-lowmem")) { + g_low_memory_threshold = gArgs.GetIntArg("-lowmem", 0 /* not used */) * 1024 * 1024; + } + if (g_low_memory_threshold > 0) { + LogPrintf("* Flushing caches if available system memory drops below %s MiB\n", g_low_memory_threshold / 1024 / 1024); + } + for (bool fLoaded = false; !fLoaded && !ShutdownRequested(node);) { node.mempool = std::make_unique(mempool_opts);