Skip to content

Conversation

@StephenMolloy
Copy link
Member

@StephenMolloy StephenMolloy commented Jul 21, 2025

This pull request introduces a new configuration option to the .NET System.Runtime.Caching library, allowing users to control how physical memory usage is monitored and managed by the cache. The main enhancement is the addition of a physicalMemoryMode setting, 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:

  • Added a new physicalMemoryMode configuration property to MemoryCacheElement, allowing users to select between "Legacy", "Standard", and "GCThresholds" modes for physical memory monitoring. This property is now parsed and stored in cache configuration.
  • Updated sample configuration XML to demonstrate usage of the new physicalMemoryMode option for different named caches.

Code Infrastructure and Parsing:

  • Introduced the PhysicalMemoryMode enum and integrated it into configuration parsing and validation logic. Added utility methods for parsing and handling the new enum value from configuration.
  • Extended the ConfigUtil class with methods for retrieving string values and parsing the PhysicalMemoryMode enum from configuration.

Platform-Specific Monitoring Logic:

  • Added a new file, MemoryMonitor.Unix.cs,which corrects our previously incorrect initializing of total physical memory information for Unix platforms using GC.GetGCMemoryInfo().
  • Refactored Unix and Windows implementations of PhysicalMemoryMonitor to 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 new Standard mode 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 GCThresholds mode. GCThresholds mode 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 using GC.GetGCMemoryInfo() without inducing collections)—GCThresholds fundamentally 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.

@StephenMolloy StephenMolloy marked this pull request as ready for review November 26, 2025 17:29
Copilot AI review requested due to automatic review settings November 26, 2025 17:29
Copilot finished reviewing on behalf of StephenMolloy November 26, 2025 17:32
@StephenMolloy StephenMolloy marked this pull request as draft November 26, 2025 17:34
Copy link
Contributor

Copilot AI left a 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 PhysicalMemoryMode enum with Legacy, Standard, and GCThresholds modes
  • Adds configuration parsing and validation for the new physicalMemoryMode setting
  • 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

{
if (!Enum.TryParse<PhysicalMemoryMode>(rawValue, true, out PhysicalMemoryMode mode))
{
throw new ArgumentException(null, ConfigUtil.PhysicalMemoryMode);
Copy link

Copilot AI Nov 26, 2025

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.

Suggested change
throw new ArgumentException(null, ConfigUtil.PhysicalMemoryMode);
throw new ArgumentException(RH.Format(SR.Value_must_be_valid_enum, ConfigUtil.PhysicalMemoryMode, rawValue), ConfigUtil.PhysicalMemoryMode);

Copilot uses AI. Check for mistakes.
@StephenMolloy StephenMolloy changed the title Add 'physicalMemoryAvailableBytes' config setting. Follow GC when set… Add 'physicalMemoryMode' to SRC.MemoryCache. New mode will follow GC metrics to determine trimming. Nov 27, 2025
Copy link
Contributor

Copilot AI left a 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
Copy link

Copilot AI Dec 4, 2025

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".

Suggested change
// 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

Copilot uses AI. Check for mistakes.
Comment on lines +5 to +7
using System.Collections.Specialized;
using System.Security;
using System.Runtime.InteropServices;
Copy link

Copilot AI Dec 4, 2025

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.Specialized
  • System.Security
  • System.Runtime.InteropServices

Only System is actually used in this file.

Suggested change
using System.Collections.Specialized;
using System.Security;
using System.Runtime.InteropServices;

Copilot uses AI. Check for mistakes.
using System.Security;
using System.Runtime.InteropServices;


Copy link

Copilot AI Dec 4, 2025

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.

Suggested change

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +73
if (_physicalMemoryMode == PhysicalMemoryMode.GCThresholds)
{
// GCThresholds mode always targets HighMemoryLoadThresholdBytes, but we can still adjust the low threshold.
_pressureHigh = 100;
_pressureLow = Math.Max(3, physicalMemoryLimitPercentage);
Copy link

Copilot AI Dec 4, 2025

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.

Copilot uses AI. Check for mistakes.
Comment on lines +23 to +28
// 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.
Copy link

Copilot AI Dec 4, 2025

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.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant