public bool Trim() { Debug.Assert(s_trimBuffers); Debug.Assert(s_allTlsBuckets != null); int milliseconds = Environment.TickCount; MemoryPressure pressure = GetMemoryPressure(); ArrayPoolEventSource log = ArrayPoolEventSource.Log; if (log.IsEnabled()) { log.BufferTrimPoll(milliseconds, (int)pressure); } PerCoreLockedStacks?[] perCoreBuckets = _buckets; for (int i = 0; i < perCoreBuckets.Length; i++) { perCoreBuckets[i]?.Trim((uint)milliseconds, Id, pressure, _bucketArraySizes[i]); } if (pressure == MemoryPressure.High) { // Under high pressure, release all thread locals if (log.IsEnabled()) { foreach (KeyValuePair <T[]?[], object?> tlsBuckets in s_allTlsBuckets) { T[]?[] buckets = tlsBuckets.Key; for (int i = 0; i < buckets.Length; i++) { T[]? buffer = Interlocked.Exchange(ref buckets[i], null); if (buffer != null) { // As we don't want to take a perf hit in the rent path it // is possible that a buffer could be rented as we "free" it. log.BufferTrimmed(buffer.GetHashCode(), buffer.Length, Id); } } } } else { foreach (KeyValuePair <T[]?[], object?> tlsBuckets in s_allTlsBuckets) { T[]?[] buckets = tlsBuckets.Key; Array.Clear(buckets, 0, buckets.Length); } } } return(true); }
public bool Trim() { int milliseconds = Environment.TickCount; MemoryPressure pressure = GetMemoryPressure(); ArrayPoolEventSource log = ArrayPoolEventSource.Log; if (log.IsEnabled()) { log.BufferTrimPoll(milliseconds, (int)pressure); } foreach (PerCoreLockedStacks bucket in _buckets) { bucket?.Trim((uint)milliseconds, Id, pressure, _bucketArraySizes); } if (pressure == MemoryPressure.High) { // Under high pressure, release all thread locals foreach (KeyValuePair <T[][], object> tlsBuckets in s_AllTlsBuckets) { T[][] buckets = tlsBuckets.Key; for (int i = 0; i < NumBuckets; i++) { T[] buffer = buckets[i]; buckets[i] = null; if (log.IsEnabled() && buffer != null) { log.BufferTrimmed(buffer.GetHashCode(), buffer.Length, Id); } } } } return(true); }
public void Trim(uint tickCount, int id, MemoryPressure pressure, int bucketSize) { const uint StackTrimAfterMS = 60 * 1000; // Trim after 60 seconds for low/moderate pressure const uint StackHighTrimAfterMS = 10 * 1000; // Trim after 10 seconds for high pressure const uint StackRefreshMS = StackTrimAfterMS / 4; // Time bump after trimming (1/4 trim time) const int StackLowTrimCount = 1; // Trim one item when pressure is low const int StackMediumTrimCount = 2; // Trim two items when pressure is moderate const int StackHighTrimCount = MaxBuffersPerArraySizePerCore; // Trim all items when pressure is high const int StackLargeBucket = 16384; // If the bucket is larger than this we'll trim an extra when under high pressure const int StackModerateTypeSize = 16; // If T is larger than this we'll trim an extra when under high pressure const int StackLargeTypeSize = 32; // If T is larger than this we'll trim an extra (additional) when under high pressure if (_count == 0) { return; } uint trimTicks = pressure == MemoryPressure.High ? StackHighTrimAfterMS : StackTrimAfterMS; lock (this) { if (_count > 0 && _firstStackItemMS > tickCount || (tickCount - _firstStackItemMS) > trimTicks) { // We've wrapped the tick count or elapsed enough time since the // first item went into the stack. Drop the top item so it can // be collected and make the stack look a little newer. ArrayPoolEventSource log = ArrayPoolEventSource.Log; int trimCount = StackLowTrimCount; switch (pressure) { case MemoryPressure.High: trimCount = StackHighTrimCount; // When pressure is high, aggressively trim larger arrays. if (bucketSize > StackLargeBucket) { trimCount++; } if (Unsafe.SizeOf <T>() > StackModerateTypeSize) { trimCount++; } if (Unsafe.SizeOf <T>() > StackLargeTypeSize) { trimCount++; } break; case MemoryPressure.Medium: trimCount = StackMediumTrimCount; break; } while (_count > 0 && trimCount-- > 0) { T[]? array = _arrays[--_count]; Debug.Assert(array != null, "No nulls should have been present in slots < _count."); _arrays[_count] = null; if (log.IsEnabled()) { log.BufferTrimmed(array.GetHashCode(), array.Length, id); } } if (_count > 0 && _firstStackItemMS < uint.MaxValue - StackRefreshMS) { // Give the remaining items a bit more time _firstStackItemMS += StackRefreshMS; } } } }