Skip to content

MemoryCache entry eviction is based on system memory usage, not cache memory usage #114714

Open
@karimsalem1

Description

@karimsalem1

Description

MemoryCache accepts a PhysicalMemoryLimitPercentage property which is documented as

The percentage of physical memory that the cache can use, expressed as an integer value from 1 to 100. If the cache size exceeds the specified limit, the memory cache implementation removes cache entries.

This implies that if the cache is consuming X% of the machine's physical memory, the cache will start evicting entries to reduce memory pressure.

However, the actual behavior is different: the implementation monitors total system-wide physical memory usage — not the memory used by the cache. As a result, cache entries may be evicted even if the cache is consuming a trivial amount of memory, simply because overall system memory is under pressure.

We discovered this issue in production as the MemoryCache was wrongly evicting items despite the insignificant cache size and the physicalMemoryLimitPercentage set to 40%.

Reproduction Steps

  1. Instantiate a MemoryCache instance with physicalMemoryLimitPercentage set to a value lower than your machine's current system memory usage (can be found in TaskManager > Performance > Memory).
  2. Periodically add items to a small cache without expiration policies, registering a RemovedCallback to log entry removals to the console along with their RemovedReason.
  3. Observe that items are being removed from the cache with RemovedReason set to Evicted, despite the cache itself not consuming significant memory.

Repro file added: MemoryCacheTest.txt

Expected behavior

The entries should not be evicted from the cache, because the cache size is very small. It does not add up to the physical memory limit percentage defined.

Actual behavior

Cache entries are being regularly evicted because there is overall memory pressure on the machine.

Regression?

Unlikely to be a regression.

Known Workarounds

Set PhysicalMemoryLimitPercentage to 0 or a sufficiently high value to minimize unwanted cache evictions triggered by system-wide memory pressure.

Configuration

  • Reproable in .NET 8 and .NET Framework 4.8.
  • Microsoft Windows 11 Enterprise - 10.0.22631 Build 22631
  • x64
  • Should not be specific to this configuration.

Other information

The relevant code can be found in PhysicalMemoryMonitor.cs. The Windows implementation uses the GlobalMemoryStatusEx to retrieve the system-wide physical memory usage, rather than the cache's actual memory footprint.

This seems to be intentional, as noted in the class comment:

PhysicalMemoryMonitor monitors the amount of physical memory used on the machine and helps us determine when to drop entries to avoid paging and GC thrashing.

Therefore, this is likely not a code bug, but rather a documentation issue. The current behavior may lead to confusion for service owners who interpret the logic as tracking cache memory usage, potentially resulting in unexpected cache eviction behavior.

Proposed resolutions:

  • Clarify the documentation to reflect the current design, making it explicit that eviction decisions are based on global memory pressure, not cache-specific usage.
  • Update the implementation to reflect cache memory usage, if that was the intended behavior, although that would be a breaking change.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions