MemoryCache在項目中用了好久,感受比較簡單,之前也看過裏面的源代碼,主要借用MemoryCacheStore來完成數據的存儲,裏面是線程安全的,MemoryCacheStore借用Hashtable來實現存儲,若是已經有數據了,就把之前的刪除而後在添加 java
咱們來看看MemoryCache的實現:c++
public class MemoryCache : ObjectCache, IEnumerable, IDisposable { private static readonly TimeSpan OneYear = new TimeSpan(365, 0, 0, 0); private static object s_initLock = new object(); private static MemoryCache s_defaultCache; private static CacheEntryRemovedCallback s_sentinelRemovedCallback = new CacheEntryRemovedCallback(SentinelEntry.OnCacheEntryRemovedCallback); private GCHandleRef<MemoryCacheStore>[] _storeRefs; private int _storeCount; private int _disposed; private MemoryCacheStatistics _stats; private string _name; private PerfCounters _perfCounters; private bool _configLess; EventHandler _onAppDomainUnload; UnhandledExceptionEventHandler _onUnhandledException; private MemoryCache() { _name = "Default"; Init(null); } public MemoryCache(string name, NameValueCollection config = null) { if (name == null) { throw new ArgumentNullException("name"); } if (name == String.Empty) { throw new ArgumentException(R.Empty_string_invalid, "name"); } if (String.Equals(name, "default", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(R.Default_is_reserved, "name"); } _name = name; Init(config); } private void Init(NameValueCollection config) { _storeCount = Environment.ProcessorCount; _storeRefs = new GCHandleRef<MemoryCacheStore>[_storeCount]; InitDisposableMembers(config); } public override object Get(string key, string regionName = null) { return GetInternal(key, regionName); } private object GetInternal(string key, string regionName) { if (regionName != null) { throw new NotSupportedException(R.RegionName_not_supported); } if (key == null) { throw new ArgumentNullException("key"); } MemoryCacheEntry entry = GetEntry(key); return (entry != null) ? entry.Value : null; } internal MemoryCacheEntry GetEntry(String key) { if (IsDisposed) { return null; } MemoryCacheKey cacheKey = new MemoryCacheKey(key); MemoryCacheStore store = GetStore(cacheKey); return store.Get(cacheKey); } public override void Set(string key, object value, CacheItemPolicy policy, string regionName = null) { if (regionName != null) { throw new NotSupportedException(R.RegionName_not_supported); } if (key == null) { throw new ArgumentNullException("key"); } DateTimeOffset absExp = ObjectCache.InfiniteAbsoluteExpiration; TimeSpan slidingExp = ObjectCache.NoSlidingExpiration; CacheItemPriority priority = CacheItemPriority.Default; Collection<ChangeMonitor> changeMonitors = null; CacheEntryRemovedCallback removedCallback = null; if (policy != null) { ValidatePolicy(policy); if (policy.UpdateCallback != null) { Set(key, value, policy.ChangeMonitors, policy.AbsoluteExpiration, policy.SlidingExpiration, policy.UpdateCallback); return; } absExp = policy.AbsoluteExpiration; slidingExp = policy.SlidingExpiration; priority = policy.Priority; changeMonitors = policy.ChangeMonitors; removedCallback = policy.RemovedCallback; } if (IsDisposed) { if (changeMonitors != null) { foreach (ChangeMonitor monitor in changeMonitors) { if (monitor != null) { monitor.Dispose(); } } } return; } MemoryCacheKey cacheKey = new MemoryCacheKey(key); MemoryCacheStore store = GetStore(cacheKey); store.Set(cacheKey, new MemoryCacheEntry(key, value, absExp, slidingExp, priority, changeMonitors, removedCallback, this)); } internal MemoryCacheStore GetStore(MemoryCacheKey cacheKey) { // Dev10 865907: Math.Abs throws OverflowException for Int32.MinValue int hashCode = cacheKey.Hash; if (hashCode < 0) { hashCode = (hashCode == Int32.MinValue) ? 0 : -hashCode; } int idx = hashCode % _storeCount; return _storeRefs[idx].Target; } private void InitDisposableMembers(NameValueCollection config) { bool dispose = true; try { try { _perfCounters = new PerfCounters(_name); } catch { // ignore exceptions from perf counters } for (int i = 0; i < _storeCount; i++) { _storeRefs[i] = new GCHandleRef<MemoryCacheStore> (new MemoryCacheStore(this, _perfCounters)); } _stats = new MemoryCacheStatistics(this, config); AppDomain appDomain = Thread.GetDomain(); EventHandler onAppDomainUnload = new EventHandler(OnAppDomainUnload); appDomain.DomainUnload += onAppDomainUnload; _onAppDomainUnload = onAppDomainUnload; UnhandledExceptionEventHandler onUnhandledException = new UnhandledExceptionEventHandler(OnUnhandledException); appDomain.UnhandledException += onUnhandledException; _onUnhandledException = onUnhandledException; dispose = false; } finally { if (dispose) { Dispose(); } } } private void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) { Dispose(); } private void OnUnhandledException(Object sender, UnhandledExceptionEventArgs eventArgs) { // if the CLR is terminating, dispose the cache. // This will dispose the perf counters (see Dev10 680819). if (eventArgs.IsTerminating) { Dispose(); } } public void Dispose() { if (Interlocked.Exchange(ref _disposed, 1) == 0) { // unhook domain events DisposeSafeCritical(); // stats must be disposed prior to disposing the stores. if (_stats != null) { _stats.Dispose(); } if (_storeRefs != null) { foreach (var storeRef in _storeRefs) { if (storeRef != null) { storeRef.Dispose(); } } } if (_perfCounters != null) { _perfCounters.Dispose(); } GC.SuppressFinalize(this); } } }
MemoryCacheStore的實現:web
internal sealed class MemoryCacheStore : IDisposable { const int INSERT_BLOCK_WAIT = 10000; const int MAX_COUNT = Int32.MaxValue / 2; private Hashtable _entries; private Object _entriesLock; private CacheExpires _expires; private CacheUsage _usage; private int _disposed; private ManualResetEvent _insertBlock; private volatile bool _useInsertBlock; private MemoryCache _cache; private PerfCounters _perfCounters; internal MemoryCacheStore(MemoryCache cache, PerfCounters perfCounters) { _cache = cache; _perfCounters = perfCounters; _entries = new Hashtable(new MemoryCacheEqualityComparer()); _entriesLock = new Object(); _expires = new CacheExpires(this); _usage = new CacheUsage(this); InitDisposableMembers(); } internal MemoryCacheEntry Get(MemoryCacheKey key) { MemoryCacheEntry entry = _entries[key] as MemoryCacheEntry; // has it expired? if (entry != null && entry.UtcAbsExp <= DateTime.UtcNow) { Remove(key, entry, CacheEntryRemovedReason.Expired); entry = null; } // update outside of lock UpdateExpAndUsage(entry); return entry; } internal void Set(MemoryCacheKey key, MemoryCacheEntry entry) { if (_useInsertBlock && entry.HasUsage()) { WaitInsertBlock(); } MemoryCacheEntry existingEntry = null; bool added = false; lock (_entriesLock) { if (_disposed == 0) { existingEntry = _entries[key] as MemoryCacheEntry; if (existingEntry != null) { existingEntry.State = EntryState.RemovingFromCache; } entry.State = EntryState.AddingToCache; added = true; _entries[key] = entry; } } CacheEntryRemovedReason reason = CacheEntryRemovedReason.Removed; if (existingEntry != null) { if (existingEntry.UtcAbsExp <= DateTime.UtcNow) { reason = CacheEntryRemovedReason.Expired; } RemoveFromCache(existingEntry, reason, delayRelease:true); } if (added) { AddToCache(entry); } // Dev10 861163: Call Release after the new entry has been completely added so // that the CacheItemRemovedCallback can take a dependency on the newly inserted item. if (existingEntry != null) { existingEntry.Release(_cache, reason); } } private void AddToCache(MemoryCacheEntry entry) { // add outside of lock if (entry != null) { if (entry.HasExpiration()) { _expires.Add(entry); } if (entry.HasUsage() && (!entry.HasExpiration() || entry.UtcAbsExp - DateTime.UtcNow >= CacheUsage.MIN_LIFETIME_FOR_USAGE)) { _usage.Add(entry); } entry.State = EntryState.AddedToCache; entry.CallNotifyOnChanged(); if (_perfCounters != null) { _perfCounters.Increment(PerfCounterName.Entries); _perfCounters.Increment(PerfCounterName.Turnover); } } } internal void UpdateExpAndUsage(MemoryCacheEntry entry, bool updatePerfCounters = true) { if (entry != null) { if (entry.InUsage() || entry.SlidingExp > TimeSpan.Zero) { DateTime utcNow = DateTime.UtcNow; entry.UpdateSlidingExp(utcNow, _expires); entry.UpdateUsage(utcNow, _usage); } // DevDiv #67021: If this entry has an update sentinel, the sliding expiration is actually associated // with that sentinel, not with this entry. We need to update the sentinel's sliding expiration to // keep the sentinel from expiring, which in turn would force a removal of this entry from the cache. entry.UpdateSlidingExpForUpdateSentinel(); if (updatePerfCounters && _perfCounters != null) { _perfCounters.Increment(PerfCounterName.Hits); _perfCounters.Increment(PerfCounterName.HitRatio); _perfCounters.Increment(PerfCounterName.HitRatioBase); } } else { if (updatePerfCounters && _perfCounters != null) { _perfCounters.Increment(PerfCounterName.Misses); _perfCounters.Increment(PerfCounterName.HitRatioBase); } } } }
可見MemoryCache和MemoryCacheStore的實現都很是好理解。咱們以web程序爲例, 全部的數據都存在MemoryCacheStore的Hashtable中,可是不一樣的請求如何共享這個MemoryCacheStore數據了,通常咱們採用static變量來實現,static變量跟着進程走,裏面的線程共享它,咱們來看看MemoryCache,它不是用靜態變量,而是採用GCHandle來實現的,裏面封裝的GCHandleRef:c#
internal class GCHandleRef<T> : IDisposable where T : class, IDisposable { GCHandle _handle; T _t; [SecuritySafeCritical] [PermissionSet(SecurityAction.Assert, Unrestricted = true)] public GCHandleRef(T t) { _handle = GCHandle.Alloc(t); } public T Target { [SecuritySafeCritical] [PermissionSet(SecurityAction.Assert, Unrestricted = true)] get { try { T t = (T)_handle.Target; if (t != null) { return t; } } catch (InvalidOperationException) { // use the normal reference instead of throwing an exception when _handle is already freed } return _t; } } [SecuritySafeCritical] [PermissionSet(SecurityAction.Assert, Unrestricted = true)] public void Dispose() { Target.Dispose(); // Safe to call Dispose more than once but not thread-safe if (_handle.IsAllocated) { // We must free the GC handle to avoid leaks. // However after _handle is freed we no longer have access to its Target // which will cause AVs and various race conditions under stress. // We revert to using normal references after disposing the GC handle _t = (T)_handle.Target; _handle.Free(); } } }
在MemoryCache的MemoryCache構造函數裏面會調用Init方法,裏面會初始化_storeRefs數組( _storeRefs = new GCHandleRef<MemoryCacheStore>[_storeCount];),最主要的是還調用InitDisposableMembers方法,在InitDisposableMembers方法裏面給每一個_storeRefs初始化一個實例,這些實例不會賠GC自動回收,而是在AppDomain的DomainUnload和UnhandledException事件裏回收,回收也採用了原子鎖。數組
咱們在使用c#託管代碼時,內存地址和GC回收不是咱們關心的,CLR已經給咱們進行了暗箱操做。可是有時候咱們想使用相似C語言那種方式直接對內存進行操做,或者涉及到非託管代碼的調用,此時就須要保護內存地址,防止GC垃圾回收機制將內存收回。由於一旦內存被CLR回收掉,直接形成非託管代碼再次訪問這塊內存時失效,致使程序崩潰。安全
C#中直接操做內存主要有如下三種方法:
一、GCHandle類用於提供用於從非託管內存訪問託管對象的方法。下面經過程序進行介紹:app
//託管的內存區域
Int16[] Mangement_Mem = new Int16[4]{ 4, 3, 2, 1 };
GCHandle gch = GCHandle.Alloc(Mangement_Mem,GCHandleType.Normal);
/*
爲託管內存Mangement_Mem分配GCHandle句柄,它保護Mangement_Mem對象不被垃圾回收。可是此時Mangement_Mem在內存中的地址可能會改變,無論內存如何改變,其對象的的句柄的整數表示即gch值是不變的,所以能夠將其值傳給非託管函數中去使用。當再也不須要 GCHandle時,必須經過Free將其釋放,此後GC垃圾處理器可能纔會對其回收。
*/
/*
GCHandle.Alloc(Mangement_Mem,GCHandleType.Normal)做用相似以下:
GC.KeepAlive(Mangement_Mem);
從Mangement_Mem句柄表現形式再次轉化爲句GCHandle對象
IntPtr Ptr_Mem = GCHandle.ToIntPtr(gch);
GCHandle handle = GCHandle.FromIntPtr(Ptr_Mem);
*/
//獲取該GCHandle對象表示的實際對象。
Int16[] array = (Int16[]) handle.Target;
GCHandle.Alloc(Mangement_Mem,GCHandleType.Normal);GCHandle.Alloc函數的第二個形參,除了有GCHandleType.Normal 外,還有Pinned。但Normal不會固定其地址,只是保證內存不被GC回收。而Pinned能夠將地址固定住,Pinned後這將防止垃圾回收器移動內存地址。dom
二、 Marshal
C#中提供了一個方法集,這些方法用於分配非託管內存、複製非託管內存塊、將託管類型轉換爲非託管類型,此外還提供了在與非託管代碼交互時使用的其餘雜項方法。也只有c++.net纔有託管,非託管的概念,純的C++沒有這個概念。java能夠認爲全部東西都是託管的。這就是經過marshal類實現。ide
Marshal能夠實現結構體和字節序之間的轉化。具體能夠搜索一下網上的資料。函數
三、經過fixe固定地址。將咱們申請的資源經過關鍵字進行固定,達到使CLR不使用垃圾回收機制操做咱們保護的內存。
class StudentInfo { public string Name { set; get; } } class Program { private const int OptionsMask = 0xFFFF; static void Main(string[] args) { var a = new StudentInfo { Name = "Gavin" }; var b = ObjectToByte(a); var c = ByteToObject(ref a, b); Console.ReadKey(); } /// <summary> /// 將結構體轉換成字節數組 /// </summary> /// <param name="obj"></param> /// <returns></returns> public static byte[] ObjectToByte<T>(T obj) { //獲得結構體的大小 int size = Marshal.SizeOf<T>(obj); //建立byte數組 byte[] bytes = new byte[size]; //分配結構體大小的內存空間 IntPtr structPtr = Marshal.AllocHGlobal(size); //將結構體拷到分配好的內存空間 Marshal.StructureToPtr(obj, structPtr, false); //從內存空間拷到byte數組 Marshal.Copy(structPtr, bytes, 0, size); //釋放內存空間 Marshal.FreeHGlobal(structPtr); //返回byte數組 return bytes; } /// <summary> /// 將字節數組轉換成結構體 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="_struct"></param> /// <param name="buffer"></param> /// <returns></returns> public static int ByteToObject<T>(ref T _struct, byte[] buffer) { try { if ((buffer != null) && (buffer.Length > 0)) { GCHandle pinned = GCHandle.Alloc(buffer, GCHandleType.Pinned); try { _struct = (T)Marshal.PtrToStructure(pinned.AddrOfPinnedObject(), typeof(T)); return buffer.Length; } finally { pinned.Free(); } } else return 0; } catch { return -1; } } }