You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@ -0,0 +1,308 @@
|
||||
// <copyright file="MemoryCacheStats.cs" company="Microsoft">
|
||||
// Copyright (c) 2009 Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
using System.Collections.Specialized;
|
||||
using System.Configuration;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Globalization;
|
||||
using System.Runtime.Caching.Configuration;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Runtime.Caching {
|
||||
internal sealed class MemoryCacheStatistics : IDisposable {
|
||||
const int MEMORYSTATUS_INTERVAL_5_SECONDS = 5 * 1000;
|
||||
const int MEMORYSTATUS_INTERVAL_30_SECONDS = 30 * 1000;
|
||||
|
||||
private int _configCacheMemoryLimitMegabytes;
|
||||
private int _configPhysicalMemoryLimitPercentage;
|
||||
private int _configPollingInterval;
|
||||
private int _inCacheManagerThread;
|
||||
private int _disposed;
|
||||
private long _lastTrimCount;
|
||||
private long _lastTrimDurationTicks; // used only for debugging
|
||||
private int _lastTrimGen2Count;
|
||||
private int _lastTrimPercent;
|
||||
private DateTime _lastTrimTime;
|
||||
private int _pollingInterval;
|
||||
private Timer _timer;
|
||||
private Object _timerLock;
|
||||
private long _totalCountBeforeTrim;
|
||||
|
||||
private CacheMemoryMonitor _cacheMemoryMonitor;
|
||||
private MemoryCache _memoryCache;
|
||||
private PhysicalMemoryMonitor _physicalMemoryMonitor;
|
||||
|
||||
// private
|
||||
|
||||
private MemoryCacheStatistics() {
|
||||
//hide default ctor
|
||||
}
|
||||
|
||||
private void AdjustTimer() {
|
||||
lock (_timerLock) {
|
||||
|
||||
if (_timer == null)
|
||||
return;
|
||||
|
||||
// the order of these if statements is important
|
||||
|
||||
// When above the high pressure mark, interval should be 5 seconds or less
|
||||
if (_physicalMemoryMonitor.IsAboveHighPressure() || _cacheMemoryMonitor.IsAboveHighPressure()) {
|
||||
if (_pollingInterval > MEMORYSTATUS_INTERVAL_5_SECONDS) {
|
||||
_pollingInterval = MEMORYSTATUS_INTERVAL_5_SECONDS;
|
||||
_timer.Change(_pollingInterval, _pollingInterval);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// When above half the low pressure mark, interval should be 30 seconds or less
|
||||
if ((_cacheMemoryMonitor.PressureLast > _cacheMemoryMonitor.PressureLow / 2)
|
||||
|| (_physicalMemoryMonitor.PressureLast > _physicalMemoryMonitor.PressureLow / 2)) {
|
||||
// DevDivBugs 104034: allow interval to fall back down when memory pressure goes away
|
||||
int newPollingInterval = Math.Min(_configPollingInterval, MEMORYSTATUS_INTERVAL_30_SECONDS);
|
||||
if (_pollingInterval != newPollingInterval) {
|
||||
_pollingInterval = newPollingInterval;
|
||||
_timer.Change(_pollingInterval, _pollingInterval);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// there is no pressure, interval should be the value from config
|
||||
if (_pollingInterval != _configPollingInterval) {
|
||||
_pollingInterval = _configPollingInterval;
|
||||
_timer.Change(_pollingInterval, _pollingInterval);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// timer callback
|
||||
private void CacheManagerTimerCallback(object state) {
|
||||
CacheManagerThread(0);
|
||||
}
|
||||
|
||||
private int GetPercentToTrim() {
|
||||
int gen2Count = GC.CollectionCount(2);
|
||||
// has there been a Gen 2 Collection since the last trim?
|
||||
if (gen2Count != _lastTrimGen2Count) {
|
||||
return Math.Max(_physicalMemoryMonitor.GetPercentToTrim(_lastTrimTime, _lastTrimPercent), _cacheMemoryMonitor.GetPercentToTrim(_lastTrimTime, _lastTrimPercent));
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeConfiguration(NameValueCollection config) {
|
||||
MemoryCacheElement element = null;
|
||||
if (!_memoryCache.ConfigLess) {
|
||||
MemoryCacheSection section = ConfigurationManager.GetSection("system.runtime.caching/memoryCache") as MemoryCacheSection;
|
||||
if (section != null) {
|
||||
element = section.NamedCaches[_memoryCache.Name];
|
||||
}
|
||||
}
|
||||
|
||||
if (element != null) {
|
||||
_configCacheMemoryLimitMegabytes = element.CacheMemoryLimitMegabytes;
|
||||
_configPhysicalMemoryLimitPercentage = element.PhysicalMemoryLimitPercentage;
|
||||
double milliseconds = element.PollingInterval.TotalMilliseconds;
|
||||
_configPollingInterval = (milliseconds < (double)Int32.MaxValue) ? (int) milliseconds : Int32.MaxValue;
|
||||
}
|
||||
else {
|
||||
_configPollingInterval = ConfigUtil.DefaultPollingTimeMilliseconds;
|
||||
_configCacheMemoryLimitMegabytes = 0;
|
||||
_configPhysicalMemoryLimitPercentage = 0;
|
||||
}
|
||||
|
||||
if (config != null) {
|
||||
_configPollingInterval = ConfigUtil.GetIntValueFromTimeSpan(config, ConfigUtil.PollingInterval, _configPollingInterval);
|
||||
_configCacheMemoryLimitMegabytes = ConfigUtil.GetIntValue(config, ConfigUtil.CacheMemoryLimitMegabytes, _configCacheMemoryLimitMegabytes, true, Int32.MaxValue);
|
||||
_configPhysicalMemoryLimitPercentage = ConfigUtil.GetIntValue(config, ConfigUtil.PhysicalMemoryLimitPercentage, _configPhysicalMemoryLimitPercentage, true, 100);
|
||||
}
|
||||
}
|
||||
|
||||
private void InitDisposableMembers() {
|
||||
bool dispose = true;
|
||||
try {
|
||||
_cacheMemoryMonitor = new CacheMemoryMonitor(_memoryCache, _configCacheMemoryLimitMegabytes);
|
||||
_timer = new Timer(new TimerCallback(CacheManagerTimerCallback), null, _configPollingInterval, _configPollingInterval);
|
||||
dispose = false;
|
||||
}
|
||||
finally {
|
||||
if (dispose) {
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void SetTrimStats(long trimDurationTicks, long totalCountBeforeTrim, long trimCount) {
|
||||
_lastTrimDurationTicks = trimDurationTicks;
|
||||
|
||||
int gen2Count = GC.CollectionCount(2);
|
||||
// has there been a Gen 2 Collection since the last trim?
|
||||
if (gen2Count != _lastTrimGen2Count) {
|
||||
_lastTrimTime = DateTime.UtcNow;
|
||||
_totalCountBeforeTrim = totalCountBeforeTrim;
|
||||
_lastTrimCount = trimCount;
|
||||
}
|
||||
else {
|
||||
// we've done multiple trims between Gen 2 collections, so only add to the trim count
|
||||
_lastTrimCount += trimCount;
|
||||
}
|
||||
_lastTrimGen2Count = gen2Count;
|
||||
|
||||
_lastTrimPercent = (int)((_lastTrimCount * 100L) / _totalCountBeforeTrim);
|
||||
}
|
||||
|
||||
private void Update() {
|
||||
_physicalMemoryMonitor.Update();
|
||||
_cacheMemoryMonitor.Update();
|
||||
}
|
||||
|
||||
|
||||
|
||||
// public/internal
|
||||
|
||||
internal long CacheMemoryLimit {
|
||||
get {
|
||||
return _cacheMemoryMonitor.MemoryLimit;
|
||||
}
|
||||
}
|
||||
|
||||
internal long PhysicalMemoryLimit {
|
||||
get {
|
||||
return _physicalMemoryMonitor.MemoryLimit;
|
||||
}
|
||||
}
|
||||
|
||||
internal TimeSpan PollingInterval {
|
||||
get {
|
||||
return TimeSpan.FromMilliseconds(_configPollingInterval);
|
||||
}
|
||||
}
|
||||
|
||||
internal MemoryCacheStatistics(MemoryCache memoryCache, NameValueCollection config) {
|
||||
_memoryCache = memoryCache;
|
||||
_lastTrimGen2Count = -1;
|
||||
_lastTrimTime = DateTime.MinValue;
|
||||
_timerLock = new Object();
|
||||
InitializeConfiguration(config);
|
||||
_pollingInterval = _configPollingInterval;
|
||||
_physicalMemoryMonitor = new PhysicalMemoryMonitor(_configPhysicalMemoryLimitPercentage);
|
||||
InitDisposableMembers();
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
internal long CacheManagerThread(int minPercent) {
|
||||
if (Interlocked.Exchange(ref _inCacheManagerThread, 1) != 0)
|
||||
return 0;
|
||||
try {
|
||||
if (_disposed == 1) {
|
||||
return 0;
|
||||
}
|
||||
#if DBG
|
||||
Dbg.Trace("MemoryCacheStats", "**BEG** CacheManagerThread " + DateTime.Now.ToString("T", CultureInfo.InvariantCulture));
|
||||
#endif
|
||||
// The timer thread must always call Update so that the CacheManager
|
||||
// knows the size of the cache.
|
||||
Update();
|
||||
AdjustTimer();
|
||||
|
||||
int percent = Math.Max(minPercent, GetPercentToTrim());
|
||||
long beginTotalCount = _memoryCache.GetCount();
|
||||
Stopwatch sw = Stopwatch.StartNew();
|
||||
long trimmedOrExpired = _memoryCache.Trim(percent);
|
||||
sw.Stop();
|
||||
// 1) don't update stats if the trim happend because MAX_COUNT was exceeded
|
||||
// 2) don't update stats unless we removed at least one entry
|
||||
if (percent > 0 && trimmedOrExpired > 0) {
|
||||
SetTrimStats(sw.Elapsed.Ticks, beginTotalCount, trimmedOrExpired);
|
||||
}
|
||||
|
||||
#if DBG
|
||||
Dbg.Trace("MemoryCacheStats", "**END** CacheManagerThread: "
|
||||
+ ", percent=" + percent
|
||||
+ ", beginTotalCount=" + beginTotalCount
|
||||
+ ", trimmed=" + trimmedOrExpired
|
||||
+ ", Milliseconds=" + sw.ElapsedMilliseconds);
|
||||
#endif
|
||||
|
||||
#if PERF
|
||||
SafeNativeMethods.OutputDebugString("CacheCommon.CacheManagerThread:"
|
||||
+ " minPercent= " + minPercent
|
||||
+ ", percent= " + percent
|
||||
+ ", beginTotalCount=" + beginTotalCount
|
||||
+ ", trimmed=" + trimmedOrExpired
|
||||
+ ", Milliseconds=" + sw.ElapsedMilliseconds + "\n");
|
||||
#endif
|
||||
return trimmedOrExpired;
|
||||
}
|
||||
finally {
|
||||
Interlocked.Exchange(ref _inCacheManagerThread, 0);
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
if (Interlocked.Exchange(ref _disposed, 1) == 0) {
|
||||
lock (_timerLock) {
|
||||
Timer timer = _timer;
|
||||
if (timer != null && Interlocked.CompareExchange(ref _timer, null, timer) == timer) {
|
||||
timer.Dispose();
|
||||
Dbg.Trace("MemoryCacheStats", "Stopped CacheMemoryTimers");
|
||||
}
|
||||
}
|
||||
while (_inCacheManagerThread != 0) {
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
if (_cacheMemoryMonitor != null) {
|
||||
_cacheMemoryMonitor.Dispose();
|
||||
}
|
||||
// Don't need to call GC.SuppressFinalize(this) for sealed types without finalizers.
|
||||
}
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Grandfathered suppression from original caching code checkin")]
|
||||
internal void UpdateConfig(NameValueCollection config) {
|
||||
int pollingInterval = ConfigUtil.GetIntValueFromTimeSpan(config, ConfigUtil.PollingInterval, _configPollingInterval);
|
||||
int cacheMemoryLimitMegabytes = ConfigUtil.GetIntValue(config, ConfigUtil.CacheMemoryLimitMegabytes, _configCacheMemoryLimitMegabytes, true, Int32.MaxValue);
|
||||
int physicalMemoryLimitPercentage = ConfigUtil.GetIntValue(config, ConfigUtil.PhysicalMemoryLimitPercentage, _configPhysicalMemoryLimitPercentage, true, 100);
|
||||
|
||||
if (pollingInterval != _configPollingInterval) {
|
||||
lock (_timerLock) {
|
||||
_configPollingInterval = pollingInterval;
|
||||
}
|
||||
}
|
||||
|
||||
if (cacheMemoryLimitMegabytes == _configCacheMemoryLimitMegabytes
|
||||
&& physicalMemoryLimitPercentage == _configPhysicalMemoryLimitPercentage) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
try {
|
||||
}
|
||||
finally {
|
||||
// prevent ThreadAbortEx from interrupting
|
||||
while (Interlocked.Exchange(ref _inCacheManagerThread, 1) != 0) {
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
if (_disposed == 0) {
|
||||
if (cacheMemoryLimitMegabytes != _configCacheMemoryLimitMegabytes) {
|
||||
_cacheMemoryMonitor.SetLimit(cacheMemoryLimitMegabytes);
|
||||
_configCacheMemoryLimitMegabytes = cacheMemoryLimitMegabytes;
|
||||
}
|
||||
if (physicalMemoryLimitPercentage != _configPhysicalMemoryLimitPercentage) {
|
||||
_physicalMemoryMonitor.SetLimit(physicalMemoryLimitPercentage);
|
||||
_configPhysicalMemoryLimitPercentage = physicalMemoryLimitPercentage;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally {
|
||||
Interlocked.Exchange(ref _inCacheManagerThread, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user