C# MemoryCache GCHandle

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;
            }
        }

    }
相關文章
相關標籤/搜索