/// <summary> /// Executes a collection cycle on the pages in this list. /// </summary> /// <param name="shiftLevel">the number of bits to shift the referenced counter by. /// Value may be zero but cannot be negative</param> /// <param name="excludedList">A set of values to exclude from the collection process</param> /// <param name="e">Arguments for the collection.</param> /// <returns>The number of pages returned to the memory pool</returns> /// <remarks>If the collection mode is Emergency or Critical, it will only release the required number of pages and no more</remarks> //ToDo: Since i'll be parsing the entire list, rebuilding a new sorted tree may be quicker than removing individual blocks and copying. //ToDo: Also, I should probably change the ShouldCollect callback to an IEnumerable<int>. public int DoCollection(int shiftLevel, HashSet<int> excludedList, CollectionEventArgs e) { if (m_disposed) throw new ObjectDisposedException(GetType().FullName); if (shiftLevel < 0) throw new ArgumentOutOfRangeException("shiftLevel", "must be non negative"); int collectionCount = 0; int maxCollectCount = -1; if (e.CollectionMode == MemoryPoolCollectionMode.Emergency || e.CollectionMode == MemoryPoolCollectionMode.Critical) { maxCollectCount = e.DesiredPageReleaseCount; } for (int x = 0; x < m_pageIndexLookupByPositionIndex.Count; x++) { int pageIndex = m_pageIndexLookupByPositionIndex.Values[x]; InternalPageMetaData block = m_listOfPages.GetValue(pageIndex); block.ReferencedCount >>= shiftLevel; m_listOfPages.OverwriteValue(pageIndex, block); if (block.ReferencedCount == 0) { if (maxCollectCount != collectionCount) { if (!excludedList.Contains(pageIndex)) { collectionCount++; m_pageIndexLookupByPositionIndex.RemoveAt(x); x--; m_listOfPages.SetNull(pageIndex); e.ReleasePage(block.MemoryPoolIndex); } } } } return collectionCount; }
/// <summary> /// Executes a collection cycle of the pages that are unused. /// </summary> /// <returns></returns> public int DoCollection(CollectionEventArgs e) { lock (m_syncRoot) { if (m_disposed) return 0; HashSet<int> pages = new HashSet<int>(m_arrayIndexLocks.Select(pageLock => pageLock.CurrentPageIndex)); return m_pageList.DoCollection(1, pages, e); } }
/// <summary> /// Determines whether to allocate more memory or to do a collection cycle on the existing pool. /// </summary> private void RequestMoreFreeBlocks() { Stopwatch sw = new Stopwatch(); sw.Start(); StringBuilder sb = new StringBuilder(); sb.AppendLine("Collection Cycle Started"); bool lockTaken; Monitor.Enter(m_syncRoot); lockTaken = true; try { long size = CurrentCapacity; int collectionLevel = GetCollectionLevelBasedOnSize(size); long stopShrinkingLimit = CalculateStopShrinkingLimit(size); RemoveDeadEvents(); sb.Append("Level: " + GetCollectionLevelString(collectionLevel)); sb.AppendFormat(" Desired Size: {0}/{1}MB", stopShrinkingLimit >> 20, CurrentCapacity >> 20); sb.AppendLine(); for (int x = 0; x < collectionLevel; x++) { if (CurrentAllocatedSize < stopShrinkingLimit) break; CollectionEventArgs eventArgs = new CollectionEventArgs(ReleasePage, MemoryPoolCollectionMode.Normal, 0); Monitor.Exit(m_syncRoot); lockTaken = false; foreach (WeakEventHandler<CollectionEventArgs> c in m_requestCollectionEvent) { c.TryInvoke(this, eventArgs); } Monitor.Enter(m_syncRoot); lockTaken = true; sb.AppendFormat("Pass {0} Usage: {1}/{2}MB", x + 1, CurrentAllocatedSize >> 20, CurrentCapacity >> 20); sb.AppendLine(); } long currentSize = CurrentAllocatedSize; long sizeBefore = CurrentCapacity; if (m_pageList.GrowMemoryPool(currentSize + (long)(0.1 * MaximumPoolSize))) { long sizeAfter = CurrentCapacity; m_releasePageVersion++; sb.AppendFormat("Grew buffer pool {0}MB -> {1}MB", sizeBefore >> 20, sizeAfter >> 20); sb.AppendLine(); } if (m_pageList.FreeSpaceBytes < 0.05 * MaximumPoolSize) { int pagesToBeReleased = (int)((0.05 * MaximumPoolSize - m_pageList.FreeSpaceBytes) / PageSize); sb.AppendFormat("* Emergency Collection Occuring. Attempting to release {0} pages.", pagesToBeReleased); sb.AppendLine(); Log.Publish(MessageLevel.Warning, MessageFlags.PerformanceIssue, "Pool Emergency", string.Format("Memory pool is reaching an Emergency level. Desiring Pages To Release: {0}", pagesToBeReleased)); CollectionEventArgs eventArgs = new CollectionEventArgs(ReleasePage, MemoryPoolCollectionMode.Emergency, pagesToBeReleased); Monitor.Exit(m_syncRoot); lockTaken = false; foreach (WeakEventHandler<CollectionEventArgs> c in m_requestCollectionEvent) { if (eventArgs.DesiredPageReleaseCount == 0) break; c.TryInvoke(this, eventArgs); } Monitor.Enter(m_syncRoot); lockTaken = true; if (eventArgs.DesiredPageReleaseCount > 0) { sb.AppendFormat("** Critical Collection Occuring. Attempting to release {0} pages.", pagesToBeReleased); sb.AppendLine(); Log.Publish(MessageLevel.Warning, MessageFlags.PerformanceIssue, "Pool Critical", string.Format("Memory pool is reaching an Critical level. Desiring Pages To Release: {0}", eventArgs.DesiredPageReleaseCount)); eventArgs = new CollectionEventArgs(ReleasePage, MemoryPoolCollectionMode.Critical, eventArgs.DesiredPageReleaseCount); Monitor.Exit(m_syncRoot); lockTaken = false; foreach (WeakEventHandler<CollectionEventArgs> c in m_requestCollectionEvent) { if (eventArgs.DesiredPageReleaseCount == 0) break; c.TryInvoke(this, eventArgs); } Monitor.Enter(m_syncRoot); lockTaken = true; } } sw.Stop(); sb.AppendFormat("Elapsed Time: {0}ms", sw.Elapsed.TotalMilliseconds.ToString("0.0")); Log.Publish(MessageLevel.Info, "Memory Pool Collection Occured", sb.ToString()); RemoveDeadEvents(); } finally { if (lockTaken) Monitor.Exit(m_syncRoot); } }
/// <summary> /// Handles the <see cref="MemoryPool.RequestCollection"/> event. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void m_pool_RequestCollection(object sender, CollectionEventArgs e) { if (m_disposed) return; m_pageReplacementAlgorithm.DoCollection(e); if (e.CollectionMode == MemoryPoolCollectionMode.Critical) { //ToDo: actually do something differently if collection level reaches critical m_pageReplacementAlgorithm.DoCollection(e); } }
/// <summary> /// Determines whether to allocate more memory or to do a collection cycle on the existing pool. /// </summary> private void RequestMoreFreeBlocks() { Stopwatch sw = new Stopwatch(); sw.Start(); StringBuilder sb = new StringBuilder(); sb.AppendLine("Collection Cycle Started"); bool lockTaken; Monitor.Enter(m_syncRoot); lockTaken = true; try { long size = CurrentCapacity; int collectionLevel = GetCollectionLevelBasedOnSize(size); long stopShrinkingLimit = CalculateStopShrinkingLimit(size); RemoveDeadEvents(); sb.Append("Level: " + GetCollectionLevelString(collectionLevel)); sb.AppendFormat(" Desired Size: {0}/{1}MB", stopShrinkingLimit >> 20, CurrentCapacity >> 20); sb.AppendLine(); for (int x = 0; x < collectionLevel; x++) { if (CurrentAllocatedSize < stopShrinkingLimit) { break; } CollectionEventArgs eventArgs = new CollectionEventArgs(ReleasePage, MemoryPoolCollectionMode.Normal, 0); Monitor.Exit(m_syncRoot); lockTaken = false; foreach (WeakEventHandler <CollectionEventArgs> c in m_requestCollectionEvent) { c.TryInvoke(this, eventArgs); } Monitor.Enter(m_syncRoot); lockTaken = true; sb.AppendFormat("Pass {0} Usage: {1}/{2}MB", x + 1, CurrentAllocatedSize >> 20, CurrentCapacity >> 20); sb.AppendLine(); } long currentSize = CurrentAllocatedSize; long sizeBefore = CurrentCapacity; if (m_pageList.GrowMemoryPool(currentSize + (long)(0.1 * MaximumPoolSize))) { long sizeAfter = CurrentCapacity; m_releasePageVersion++; sb.AppendFormat("Grew buffer pool {0}MB -> {1}MB", sizeBefore >> 20, sizeAfter >> 20); sb.AppendLine(); } if (m_pageList.FreeSpaceBytes < 0.05 * MaximumPoolSize) { int pagesToBeReleased = (int)((0.05 * MaximumPoolSize - m_pageList.FreeSpaceBytes) / PageSize); sb.AppendFormat("* Emergency Collection Occuring. Attempting to release {0} pages.", pagesToBeReleased); sb.AppendLine(); Log.Publish(MessageLevel.Warning, MessageFlags.PerformanceIssue, "Pool Emergency", string.Format("Memory pool is reaching an Emergency level. Desiring Pages To Release: {0}", pagesToBeReleased)); CollectionEventArgs eventArgs = new CollectionEventArgs(ReleasePage, MemoryPoolCollectionMode.Emergency, pagesToBeReleased); Monitor.Exit(m_syncRoot); lockTaken = false; foreach (WeakEventHandler <CollectionEventArgs> c in m_requestCollectionEvent) { if (eventArgs.DesiredPageReleaseCount == 0) { break; } c.TryInvoke(this, eventArgs); } Monitor.Enter(m_syncRoot); lockTaken = true; if (eventArgs.DesiredPageReleaseCount > 0) { sb.AppendFormat("** Critical Collection Occuring. Attempting to release {0} pages.", pagesToBeReleased); sb.AppendLine(); Log.Publish(MessageLevel.Warning, MessageFlags.PerformanceIssue, "Pool Critical", string.Format("Memory pool is reaching an Critical level. Desiring Pages To Release: {0}", eventArgs.DesiredPageReleaseCount)); eventArgs = new CollectionEventArgs(ReleasePage, MemoryPoolCollectionMode.Critical, eventArgs.DesiredPageReleaseCount); Monitor.Exit(m_syncRoot); lockTaken = false; foreach (WeakEventHandler <CollectionEventArgs> c in m_requestCollectionEvent) { if (eventArgs.DesiredPageReleaseCount == 0) { break; } c.TryInvoke(this, eventArgs); } Monitor.Enter(m_syncRoot); lockTaken = true; } } sw.Stop(); sb.AppendFormat("Elapsed Time: {0}ms", sw.Elapsed.TotalMilliseconds.ToString("0.0")); Log.Publish(MessageLevel.Info, "Memory Pool Collection Occured", sb.ToString()); RemoveDeadEvents(); } finally { if (lockTaken) { Monitor.Exit(m_syncRoot); } } }