-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Add 'physicalMemoryMode' to SRC.MemoryCache. New mode will follow GC metrics to determine trimming. #117886
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: release/8.0
Are you sure you want to change the base?
Add 'physicalMemoryMode' to SRC.MemoryCache. New mode will follow GC metrics to determine trimming. #117886
Conversation
…ytes due to GC hanging on to free but committed pages
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request adds a new physicalMemoryMode configuration option to the System.Runtime.Caching library, enabling users to choose between three memory monitoring strategies: Legacy (platform-specific), Standard (GC-based without collection), and GCThresholds (aligned with GC's high memory threshold). The GCThresholds mode changes the reference point from a percentage of physical memory to the GC's internal high memory load threshold, making cache trimming behavior more responsive to actual GC pressure.
Key changes:
- Introduces
PhysicalMemoryModeenum with Legacy, Standard, and GCThresholds modes - Adds configuration parsing and validation for the new
physicalMemoryModesetting - Implements Unix platform initialization using
GC.GetGCMemoryInfo()for total physical memory detection - Refactors pressure calculation logic to support multiple monitoring modes with GCThresholds using heap size tracking for improved trim effectiveness
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| MemoryCacheElement.cs | Adds PhysicalMemoryMode enum and configuration property with XML documentation |
| ConfigUtil.cs | Adds helper methods to parse and retrieve the PhysicalMemoryMode setting from configuration |
| MemoryCacheStatistics.cs | Updates initialization and config update logic to handle the new physicalMemoryMode setting |
| PhysicalMemoryMonitor.cs | Core implementation of multi-mode memory monitoring with new GCThresholds mode logic and refactored pressure calculation |
| PhysicalMemoryMonitor.Unix.cs | Renames GetCurrentPressure to LegacyGetCurrentPressure and fixes comparison operator |
| PhysicalMemoryMonitor.Windows.cs | Renames GetCurrentPressure to LegacyGetCurrentPressure for consistency |
| MemoryMonitor.Unix.cs | New file that initializes total physical memory using GC.GetGCMemoryInfo() on Unix platforms |
| System.Runtime.Caching.csproj | Adds the new MemoryMonitor.Unix.cs file to the build for non-Windows platforms |
| MemoryCacheSection.cs | Updates example XML configuration to demonstrate the new physicalMemoryMode settings |
| MemoryCacheTest.cs | Adds comprehensive test coverage for the new physicalMemoryMode configuration options across all three modes |
src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs
Outdated
Show resolved
Hide resolved
| { | ||
| if (!Enum.TryParse<PhysicalMemoryMode>(rawValue, true, out PhysicalMemoryMode mode)) | ||
| { | ||
| throw new ArgumentException(null, ConfigUtil.PhysicalMemoryMode); |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ArgumentException is thrown with a null message, making it unhelpful to users. It should follow the pattern of other ConfigUtil methods and use a formatted resource string like RH.Format(SR.Value_must_be_valid_enum, valueName, rawValue) to provide clear feedback about the invalid value.
| throw new ArgumentException(null, ConfigUtil.PhysicalMemoryMode); | |
| throw new ArgumentException(RH.Format(SR.Value_must_be_valid_enum, ConfigUtil.PhysicalMemoryMode, rawValue), ConfigUtil.PhysicalMemoryMode); |
src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Runtime.Caching/tests/System.Runtime.Caching/MemoryCacheTest.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Runtime.Caching/src/System/Runtime/Caching/PhysicalMemoryMonitor.cs
Outdated
Show resolved
Hide resolved
…instead of trying to be smart in 'GetPercentToTrim()'
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 5 comments.
| Assert.True(mc.PhysicalMemoryLimit >= 95); | ||
|
|
||
| // Also, let's not try to duplicate the default-guessing logic here - but lets do grab the value MC | ||
| // calculated in this basec case, so we can double check that MC is using the same auto-calc value |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Spelling error: "basec" should be "base".
| // calculated in this basec case, so we can double check that MC is using the same auto-calc value | |
| // calculated in this base case, so we can double check that MC is using the same auto-calc value |
| using System.Collections.Specialized; | ||
| using System.Security; | ||
| using System.Runtime.InteropServices; |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused using directives. The following using directives are not needed and should be removed:
System.Collections.SpecializedSystem.SecuritySystem.Runtime.InteropServices
Only System is actually used in this file.
| using System.Collections.Specialized; | |
| using System.Security; | |
| using System.Runtime.InteropServices; |
| using System.Security; | ||
| using System.Runtime.InteropServices; | ||
|
|
||
|
|
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Extra blank line. There's an unnecessary blank line after the using directives. Per .editorconfig standards, there should be only one blank line between the using statements and the namespace declaration.
| if (_physicalMemoryMode == PhysicalMemoryMode.GCThresholds) | ||
| { | ||
| // GCThresholds mode always targets HighMemoryLoadThresholdBytes, but we can still adjust the low threshold. | ||
| _pressureHigh = 100; | ||
| _pressureLow = Math.Max(3, physicalMemoryLimitPercentage); |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In GCThresholds mode with a non-zero physicalMemoryLimitPercentage, the logic sets _pressureLow to the user-provided percentage. However, this doesn't align with the documented behavior in the PR description, which states that GCThresholds mode uses the GC's high memory load threshold as the upper limit.
When a user specifies a non-zero physicalMemoryLimitPercentage in GCThresholds mode, it's unclear what this percentage should represent, since the high threshold is always 100% (of the GC's threshold, not physical memory). This could lead to confusion where:
_pressureHigh = 100(representing GC threshold)_pressureLow = physicalMemoryLimitPercentage(e.g., 90, meaning 90% of GC threshold)
Consider adding documentation or validation to clarify the expected behavior when users combine GCThresholds mode with a custom physicalMemoryLimitPercentage, or adjust the logic in UseDefaultLimits() to be consistent with this approach.
| // 2. CacheMemoryMonitor - for setting the "Auto" memory limit of the cache size - which is never enforced anyway because we don't have SRef's in .NET Core. | ||
| // Which is to say, it's OK if 'TotalAvailableMemoryBytes' is not an exact representation of the physical memory on the machine, as long as it is in the ballpark of magnitude. | ||
| s_totalPhysical = memInfo.TotalAvailableMemoryBytes; | ||
|
|
||
| // s_totalVirtual/TotalVirtual on the other hand is only used to decide if an x86 Windows machine is running in /3GB mode or not... | ||
| // ... but only so it can appropriately set the "Auto" memory limit of the cache size - which again, is never enforced in .Net Core. |
Copilot
AI
Dec 4, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inconsistent product name: ".Net Core" should be ".NET Core" (with capital letters for both parts). This appears twice in the comments on lines 23 and 28.
This pull request introduces a new configuration option to the .NET
System.Runtime.Cachinglibrary, allowing users to control how physical memory usage is monitored and managed by the cache. The main enhancement is the addition of aphysicalMemoryModesetting, which supports multiple monitoring strategies for different platforms and scenarios. The changes include updates to configuration files, new enum definitions, logic to parse and apply the new setting, and supporting code for Unix and Windows implementations.Key changes:
Configuration Enhancements:
physicalMemoryModeconfiguration property toMemoryCacheElement, allowing users to select between "Legacy", "Standard", and "GCThresholds" modes for physical memory monitoring. This property is now parsed and stored in cache configuration.physicalMemoryModeoption for different named caches.Code Infrastructure and Parsing:
PhysicalMemoryModeenum and integrated it into configuration parsing and validation logic. Added utility methods for parsing and handling the new enum value from configuration.ConfigUtilclass with methods for retrieving string values and parsing thePhysicalMemoryModeenum from configuration.Platform-Specific Monitoring Logic:
MemoryMonitor.Unix.cs,which corrects our previously incorrect initializing of total physical memory information for Unix platforms usingGC.GetGCMemoryInfo().PhysicalMemoryMonitorto support the new memory monitoring modes, including splitting out legacy pressure calculation methods. Essentially, bringing the windows implementation in line with the new platform-agnostic way our non-windows code was working. (This is the newStandardmode that no longer relies on Windows-specific API's - and it also removes an unnecessary/ephemeral call to GC Collect.)These changes provide more flexibility and control over how the caching system responds to memory pressure, improving adaptability for different environments and workloads.
The big difference is the introduction of the
GCThresholdsmode.GCThresholdsmode uses the garbage collector's own high memory load threshold (GCMemoryInfo.HighMemoryLoadThresholdBytes) as the upper limit for cache memory pressure calculations, rather than a percentage of total available physical memory like Legacy and Standard modes. While Legacy and Standard modes are nearly identical in concept—both monitor memory load as a percentage of total available physical memory (with Legacy using platform-specific detection on older frameworks and Standard usingGC.GetGCMemoryInfo()without inducing collections)—GCThresholdsfundamentally changes the reference point by aligning the cache's memory pressure monitoring with the GC's internal threshold for high memory conditions. This means the cache will trim entries based on when the GC itself considers memory to be critically high (typically around 90% of available memory), making the cache behavior more responsive to actual GC pressure rather than an arbitrary percentage of physical RAM, and ensuring the cache doesn't hold onto entries when the GC is already struggling with memory.