169 lines
6.6 KiB
C#
169 lines
6.6 KiB
C#
|
// <copyright file="PhysicalMemoryMonitor.cs" company="Microsoft">
|
||
|
// Copyright (c) 2009 Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
using System;
|
||
|
using System.Runtime.Caching.Configuration;
|
||
|
using System.Security;
|
||
|
|
||
|
namespace System.Runtime.Caching {
|
||
|
// PhysicalMemoryMonitor monitors the amound of physical memory used on the machine
|
||
|
// and helps us determine when to drop entries to avoid paging and GC thrashing.
|
||
|
// The limit is configurable (see ConfigUtil.cs).
|
||
|
internal sealed class PhysicalMemoryMonitor : MemoryMonitor {
|
||
|
const int MIN_TOTAL_MEMORY_TRIM_PERCENT = 10;
|
||
|
static readonly long TARGET_TOTAL_MEMORY_TRIM_INTERVAL_TICKS = 5 * TimeSpan.TicksPerMinute;
|
||
|
|
||
|
// Returns the percentage of physical machine memory that can be consumed by an
|
||
|
// application before ASP.NET starts forcibly removing items from the cache.
|
||
|
internal long MemoryLimit {
|
||
|
get { return _pressureHigh; }
|
||
|
}
|
||
|
|
||
|
private PhysicalMemoryMonitor() {
|
||
|
// hide default ctor
|
||
|
}
|
||
|
|
||
|
internal PhysicalMemoryMonitor(int physicalMemoryLimitPercentage) {
|
||
|
/*
|
||
|
The chart below shows physical memory in megabytes, and the 1, 3, and 10% values.
|
||
|
When we reach "middle" pressure, we begin trimming the cache.
|
||
|
|
||
|
RAM 1% 3% 10%
|
||
|
-----------------------------
|
||
|
128 1.28 3.84 12.8
|
||
|
256 2.56 7.68 25.6
|
||
|
512 5.12 15.36 51.2
|
||
|
1024 10.24 30.72 102.4
|
||
|
2048 20.48 61.44 204.8
|
||
|
4096 40.96 122.88 409.6
|
||
|
8192 81.92 245.76 819.2
|
||
|
|
||
|
Low memory notifications from CreateMemoryResourceNotification are calculated as follows
|
||
|
(.\base\ntos\mm\initsup.c):
|
||
|
|
||
|
MiInitializeMemoryEvents() {
|
||
|
...
|
||
|
//
|
||
|
// Scale the threshold so on servers the low threshold is
|
||
|
// approximately 32MB per 4GB, capping it at 64MB.
|
||
|
//
|
||
|
|
||
|
MmLowMemoryThreshold = MmPlentyFreePages;
|
||
|
|
||
|
if (MmNumberOfPhysicalPages > 0x40000) {
|
||
|
MmLowMemoryThreshold = MI_MB_TO_PAGES (32);
|
||
|
MmLowMemoryThreshold += ((MmNumberOfPhysicalPages - 0x40000) >> 7);
|
||
|
}
|
||
|
else if (MmNumberOfPhysicalPages > 0x8000) {
|
||
|
MmLowMemoryThreshold += ((MmNumberOfPhysicalPages - 0x8000) >> 5);
|
||
|
}
|
||
|
|
||
|
if (MmLowMemoryThreshold > MI_MB_TO_PAGES (64)) {
|
||
|
MmLowMemoryThreshold = MI_MB_TO_PAGES (64);
|
||
|
}
|
||
|
...
|
||
|
|
||
|
E.g.
|
||
|
|
||
|
RAM(mb) low %
|
||
|
-------------------
|
||
|
256 20 92%
|
||
|
512 24 95%
|
||
|
768 28 96%
|
||
|
1024 32 97%
|
||
|
2048 40 98%
|
||
|
3072 48 98%
|
||
|
4096 56 99%
|
||
|
5120 64 99%
|
||
|
*/
|
||
|
|
||
|
long memory = TotalPhysical;
|
||
|
Dbg.Assert(memory != 0, "memory != 0");
|
||
|
if (memory >= 0x100000000) {
|
||
|
_pressureHigh = 99;
|
||
|
}
|
||
|
else if (memory >= 0x80000000) {
|
||
|
_pressureHigh = 98;
|
||
|
}
|
||
|
else if (memory >= 0x40000000) {
|
||
|
_pressureHigh = 97;
|
||
|
}
|
||
|
else if (memory >= 0x30000000) {
|
||
|
_pressureHigh = 96;
|
||
|
}
|
||
|
else {
|
||
|
_pressureHigh = 95;
|
||
|
}
|
||
|
|
||
|
_pressureLow = _pressureHigh - 9;
|
||
|
|
||
|
SetLimit(physicalMemoryLimitPercentage);
|
||
|
InitHistory();
|
||
|
|
||
|
// PerfCounter: Cache Percentage Machine Memory Limit Used
|
||
|
// = total physical memory used / total physical memory used limit
|
||
|
// PerfCounters.SetCounter(AppPerfCounter.CACHE_PERCENT_MACH_MEM_LIMIT_USED_BASE, _pressureHigh);
|
||
|
}
|
||
|
|
||
|
[SecuritySafeCritical]
|
||
|
protected override int GetCurrentPressure() {
|
||
|
#if MONO
|
||
|
var pc = new System.Diagnostics.PerformanceCounter ("Mono Memory", "Available Physical Memory");
|
||
|
long availableMemory = pc.RawValue;
|
||
|
|
||
|
int memoryLoad = (int) ((100 * availableMemory) / TotalPhysical);
|
||
|
#else
|
||
|
MEMORYSTATUSEX memoryStatusEx = new MEMORYSTATUSEX();
|
||
|
memoryStatusEx.Init();
|
||
|
if (UnsafeNativeMethods.GlobalMemoryStatusEx(ref memoryStatusEx) == 0)
|
||
|
return 0;
|
||
|
|
||
|
int memoryLoad = memoryStatusEx.dwMemoryLoad;
|
||
|
#endif
|
||
|
//if (_pressureHigh != 0) {
|
||
|
// PerfCounter: Cache Percentage Machine Memory Limit Used
|
||
|
// = total physical memory used / total physical memory used limit
|
||
|
//PerfCounters.SetCounter(AppPerfCounter.CACHE_PERCENT_MACH_MEM_LIMIT_USED, memoryLoad);
|
||
|
//}
|
||
|
|
||
|
return memoryLoad;
|
||
|
}
|
||
|
|
||
|
internal override int GetPercentToTrim(DateTime lastTrimTime, int lastTrimPercent) {
|
||
|
int percent = 0;
|
||
|
if (IsAboveHighPressure()) {
|
||
|
// choose percent such that we don't repeat this for ~5 (TARGET_TOTAL_MEMORY_TRIM_INTERVAL) minutes,
|
||
|
// but keep the percentage between 10 and 50.
|
||
|
DateTime utcNow = DateTime.UtcNow;
|
||
|
long ticksSinceTrim = utcNow.Subtract(lastTrimTime).Ticks;
|
||
|
if (ticksSinceTrim > 0) {
|
||
|
percent = Math.Min(50, (int)((lastTrimPercent * TARGET_TOTAL_MEMORY_TRIM_INTERVAL_TICKS) / ticksSinceTrim));
|
||
|
percent = Math.Max(MIN_TOTAL_MEMORY_TRIM_PERCENT, percent);
|
||
|
}
|
||
|
|
||
|
#if PERF
|
||
|
SafeNativeMethods.OutputDebugString(String.Format("PhysicalMemoryMonitor.GetPercentToTrim: percent={0:N}, lastTrimPercent={1:N}, secondsSinceTrim={2:N}\n",
|
||
|
percent,
|
||
|
lastTrimPercent,
|
||
|
ticksSinceTrim/TimeSpan.TicksPerSecond));
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
return percent;
|
||
|
}
|
||
|
|
||
|
internal void SetLimit(int physicalMemoryLimitPercentage) {
|
||
|
if (physicalMemoryLimitPercentage == 0) {
|
||
|
// use defaults
|
||
|
return;
|
||
|
}
|
||
|
_pressureHigh = Math.Max(3, physicalMemoryLimitPercentage);
|
||
|
_pressureLow = Math.Max(1, _pressureHigh - 9);
|
||
|
#if DBG
|
||
|
Dbg.Trace("MemoryCacheStats", "PhysicalMemoryMonitor.SetLimit: _pressureHigh=" + _pressureHigh +
|
||
|
", _pressureLow=" + _pressureLow);
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
}
|