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,259 @@
|
||||
// <copyright file="CacheMemoryMonitor.cs" company="Microsoft">
|
||||
// Copyright (c) 2009 Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
using System;
|
||||
using System.Runtime.Caching.Configuration;
|
||||
using System.Runtime.Caching.Hosting;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security;
|
||||
using System.Security.Permissions;
|
||||
using System.Threading;
|
||||
|
||||
namespace System.Runtime.Caching {
|
||||
// CacheMemoryMonitor uses the internal System.SizedReference type to determine
|
||||
// the size of the cache itselt, and helps us know when to drop entries to avoid
|
||||
// exceeding the cache's memory limit. The limit is configurable (see ConfigUtil.cs).
|
||||
internal sealed class CacheMemoryMonitor : MemoryMonitor, IDisposable {
|
||||
const long PRIVATE_BYTES_LIMIT_2GB = 800 * MEGABYTE;
|
||||
const long PRIVATE_BYTES_LIMIT_3GB = 1800 * MEGABYTE;
|
||||
const long PRIVATE_BYTES_LIMIT_64BIT = 1L * TERABYTE;
|
||||
const int SAMPLE_COUNT = 2;
|
||||
|
||||
private static IMemoryCacheManager s_memoryCacheManager;
|
||||
private static long s_autoPrivateBytesLimit = -1;
|
||||
private static long s_effectiveProcessMemoryLimit = -1;
|
||||
|
||||
private MemoryCache _memoryCache;
|
||||
private long[] _cacheSizeSamples;
|
||||
private DateTime[] _cacheSizeSampleTimes;
|
||||
private int _idx;
|
||||
private SRef _sizedRef;
|
||||
private int _gen2Count;
|
||||
private long _memoryLimit;
|
||||
|
||||
internal long MemoryLimit {
|
||||
get { return _memoryLimit; }
|
||||
}
|
||||
|
||||
private CacheMemoryMonitor() {
|
||||
// hide default ctor
|
||||
}
|
||||
|
||||
internal CacheMemoryMonitor(MemoryCache memoryCache, int cacheMemoryLimitMegabytes) {
|
||||
_memoryCache = memoryCache;
|
||||
_gen2Count = GC.CollectionCount(2);
|
||||
_cacheSizeSamples = new long[SAMPLE_COUNT];
|
||||
_cacheSizeSampleTimes = new DateTime[SAMPLE_COUNT];
|
||||
InitMemoryCacheManager();
|
||||
InitDisposableMembers(cacheMemoryLimitMegabytes);
|
||||
}
|
||||
|
||||
private void InitDisposableMembers(int cacheMemoryLimitMegabytes) {
|
||||
bool dispose = true;
|
||||
try {
|
||||
_sizedRef = new SRef(_memoryCache);
|
||||
SetLimit(cacheMemoryLimitMegabytes);
|
||||
InitHistory();
|
||||
dispose = false;
|
||||
}
|
||||
finally {
|
||||
if (dispose) {
|
||||
Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-generate the private bytes limit:
|
||||
// - On 64bit, the auto value is MIN(60% physical_ram, 1 TB)
|
||||
// - On x86, for 2GB, the auto value is MIN(60% physical_ram, 800 MB)
|
||||
// - On x86, for 3GB, the auto value is MIN(60% physical_ram, 1800 MB)
|
||||
//
|
||||
// - If it's not a hosted environment (e.g. console app), the 60% in the above
|
||||
// formulas will become 100% because in un-hosted environment we don't launch
|
||||
// other processes such as compiler, etc.
|
||||
private static long AutoPrivateBytesLimit {
|
||||
get {
|
||||
long memoryLimit = s_autoPrivateBytesLimit;
|
||||
if (memoryLimit == -1) {
|
||||
|
||||
bool is64bit = (IntPtr.Size == 8);
|
||||
|
||||
long totalPhysical = TotalPhysical;
|
||||
long totalVirtual = TotalVirtual;
|
||||
if (totalPhysical != 0) {
|
||||
long recommendedPrivateByteLimit;
|
||||
if (is64bit) {
|
||||
recommendedPrivateByteLimit = PRIVATE_BYTES_LIMIT_64BIT;
|
||||
}
|
||||
else {
|
||||
// Figure out if it's 2GB or 3GB
|
||||
|
||||
if (totalVirtual > 2 * GIGABYTE) {
|
||||
recommendedPrivateByteLimit = PRIVATE_BYTES_LIMIT_3GB;
|
||||
}
|
||||
else {
|
||||
recommendedPrivateByteLimit = PRIVATE_BYTES_LIMIT_2GB;
|
||||
}
|
||||
}
|
||||
|
||||
// use 60% of physical RAM
|
||||
long usableMemory = totalPhysical * 3 / 5;
|
||||
memoryLimit = Math.Min(usableMemory, recommendedPrivateByteLimit);
|
||||
}
|
||||
else {
|
||||
// If GlobalMemoryStatusEx fails, we'll use these as our auto-gen private bytes limit
|
||||
memoryLimit = is64bit ? PRIVATE_BYTES_LIMIT_64BIT : PRIVATE_BYTES_LIMIT_2GB;
|
||||
}
|
||||
Interlocked.Exchange(ref s_autoPrivateBytesLimit, memoryLimit);
|
||||
}
|
||||
|
||||
return memoryLimit;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
SRef sref = _sizedRef;
|
||||
if (sref != null && Interlocked.CompareExchange(ref _sizedRef, null, sref) == sref) {
|
||||
sref.Dispose();
|
||||
}
|
||||
IMemoryCacheManager memoryCacheManager = s_memoryCacheManager;
|
||||
if (memoryCacheManager != null) {
|
||||
memoryCacheManager.ReleaseCache(_memoryCache);
|
||||
}
|
||||
}
|
||||
|
||||
internal static long EffectiveProcessMemoryLimit {
|
||||
get {
|
||||
long memoryLimit = s_effectiveProcessMemoryLimit;
|
||||
if (memoryLimit == -1) {
|
||||
memoryLimit = AutoPrivateBytesLimit;
|
||||
Interlocked.Exchange(ref s_effectiveProcessMemoryLimit, memoryLimit);
|
||||
}
|
||||
return memoryLimit;
|
||||
}
|
||||
}
|
||||
|
||||
protected override int GetCurrentPressure() {
|
||||
// Call GetUpdatedTotalCacheSize to update the total
|
||||
// cache size, if there has been a recent Gen 2 Collection.
|
||||
// This update must happen, otherwise the CacheManager won't
|
||||
// know the total cache size.
|
||||
int gen2Count = GC.CollectionCount(2);
|
||||
SRef sref = _sizedRef;
|
||||
if (gen2Count != _gen2Count && sref != null) {
|
||||
// update _gen2Count
|
||||
_gen2Count = gen2Count;
|
||||
|
||||
// the SizedRef is only updated after a Gen2 Collection
|
||||
|
||||
// increment the index (it's either 1 or 0)
|
||||
Dbg.Assert(SAMPLE_COUNT == 2);
|
||||
_idx = _idx ^ 1;
|
||||
// remember the sample time
|
||||
_cacheSizeSampleTimes[_idx] = DateTime.UtcNow;
|
||||
// remember the sample value
|
||||
_cacheSizeSamples[_idx] = sref.ApproximateSize;
|
||||
#if DBG
|
||||
Dbg.Trace("MemoryCacheStats", "SizedRef.ApproximateSize=" + _cacheSizeSamples[_idx]);
|
||||
#endif
|
||||
IMemoryCacheManager memoryCacheManager = s_memoryCacheManager;
|
||||
if (memoryCacheManager != null) {
|
||||
memoryCacheManager.UpdateCacheSize(_cacheSizeSamples[_idx], _memoryCache);
|
||||
}
|
||||
}
|
||||
|
||||
// if there's no memory limit, then there's nothing more to do
|
||||
if (_memoryLimit <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long cacheSize = _cacheSizeSamples[_idx];
|
||||
|
||||
// use _memoryLimit as an upper bound so that pressure is a percentage (between 0 and 100, inclusive).
|
||||
if (cacheSize > _memoryLimit) {
|
||||
cacheSize = _memoryLimit;
|
||||
}
|
||||
|
||||
// PerfCounter: Cache Percentage Process Memory Limit Used
|
||||
// = memory used by this process / process memory limit at pressureHigh
|
||||
// Set private bytes used in kilobytes because the counter is a DWORD
|
||||
|
||||
//
|
||||
|
||||
|
||||
int result = (int)(cacheSize * 100 / _memoryLimit);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal override int GetPercentToTrim(DateTime lastTrimTime, int lastTrimPercent) {
|
||||
int percent = 0;
|
||||
if (IsAboveHighPressure()) {
|
||||
long cacheSize = _cacheSizeSamples[_idx];
|
||||
if (cacheSize > _memoryLimit) {
|
||||
percent = Math.Min(100, (int)((cacheSize - _memoryLimit) * 100L / cacheSize));
|
||||
}
|
||||
|
||||
#if PERF
|
||||
SafeNativeMethods.OutputDebugString(String.Format("CacheMemoryMonitor.GetPercentToTrim: percent={0:N}, lastTrimPercent={1:N}\n",
|
||||
percent,
|
||||
lastTrimPercent));
|
||||
#endif
|
||||
|
||||
}
|
||||
return percent;
|
||||
}
|
||||
|
||||
internal void SetLimit(int cacheMemoryLimitMegabytes) {
|
||||
long cacheMemoryLimit = cacheMemoryLimitMegabytes;
|
||||
cacheMemoryLimit = cacheMemoryLimit << MEGABYTE_SHIFT;
|
||||
|
||||
//
|
||||
_memoryLimit = 0;
|
||||
|
||||
// VSWhidbey 546381: never override what the user specifies as the limit;
|
||||
// only call AutoPrivateBytesLimit when the user does not specify one.
|
||||
if (cacheMemoryLimit == 0 && _memoryLimit == 0) {
|
||||
// Zero means we impose a limit
|
||||
_memoryLimit = EffectiveProcessMemoryLimit;
|
||||
}
|
||||
else if (cacheMemoryLimit != 0 && _memoryLimit != 0) {
|
||||
// Take the min of "cache memory limit" and the host's "process memory limit".
|
||||
_memoryLimit = Math.Min(_memoryLimit, cacheMemoryLimit);
|
||||
}
|
||||
else if (cacheMemoryLimit != 0) {
|
||||
// _memoryLimit is 0, but "cache memory limit" is non-zero, so use it as the limit
|
||||
_memoryLimit = cacheMemoryLimit;
|
||||
}
|
||||
|
||||
Dbg.Trace("MemoryCacheStats", "CacheMemoryMonitor.SetLimit: _memoryLimit=" + (_memoryLimit >> MEGABYTE_SHIFT) + "Mb");
|
||||
|
||||
if (_memoryLimit > 0) {
|
||||
_pressureHigh = 100;
|
||||
_pressureLow = 80;
|
||||
}
|
||||
else {
|
||||
_pressureHigh = 99;
|
||||
_pressureLow = 97;
|
||||
}
|
||||
|
||||
Dbg.Trace("MemoryCacheStats", "CacheMemoryMonitor.SetLimit: _pressureHigh=" + _pressureHigh +
|
||||
", _pressureLow=" + _pressureLow);
|
||||
}
|
||||
|
||||
[SecuritySafeCritical]
|
||||
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
|
||||
[SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "Grandfathered suppression from original caching code checkin")]
|
||||
private static void InitMemoryCacheManager() {
|
||||
if (s_memoryCacheManager == null) {
|
||||
IMemoryCacheManager memoryCacheManager = null;
|
||||
IServiceProvider host = ObjectCache.Host;
|
||||
if (host != null) {
|
||||
memoryCacheManager = host.GetService(typeof(IMemoryCacheManager)) as IMemoryCacheManager;
|
||||
}
|
||||
if (memoryCacheManager != null) {
|
||||
Interlocked.CompareExchange(ref s_memoryCacheManager, memoryCacheManager, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user