ObjectPool object ASP.NET Core Reuse (b)

Foreword

Previous article introduces the theory of knowledge ObjectPool, come tell us about how Microsoft.Extensions.ObjectPool is achieved.

image

Core components

ObjectPool

ObjectPool is a generic abstract interface, his abstract two methods Get and Return

  • Get method for obtaining from the pool available to the object, if the object is not available to create the object and returns it
  • Return method returns the object to the user object pool

    /// <summary>
    /// A pool of objects.
    /// </summary>
    /// <typeparam name="T">The type of objects to pool.</typeparam>
    public abstract class ObjectPool<T> where T : class
    {
        /// <summary>
        /// Gets an object from the pool if one is available, otherwise creates one.
        /// </summary>
        /// <returns>A <typeparamref name="T"/>.</returns>
        public abstract T Get();

        /// <summary>
        /// Return an object to the pool.
        /// </summary>
        /// <param name="obj">The object to add to the pool.</param>
        public abstract void Return(T obj);
    }

Object Pool Provider

ObjectPoolProvider is an abstract interface he built a generic abstract methods of generic methods and Create Create, he is a based on the default policy.


    /// <summary>
    /// A provider of <see cref="ObjectPool{T}"/> instances.
    /// </summary>
    public abstract class ObjectPoolProvider
    {
        /// <summary>
        /// Creates an <see cref="ObjectPool"/>.
        /// </summary>
        /// <typeparam name="T">The type to create a pool for.</typeparam>
        public ObjectPool<T> Create<T>() where T : class, new()
        {
            return Create<T>(new DefaultPooledObjectPolicy<T>());
        }

        /// <summary>
        /// Creates an <see cref="ObjectPool"/> with the given <see cref="IPooledObjectPolicy{T}"/>.
        /// </summary>
        /// <typeparam name="T">The type to create a pool for.</typeparam>
        public abstract ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy) where T : class;
    }
    

IPooledObjectPolicy

IPooledObjectPolicy is a generic interface that provides policy management object pool, the class also defines two methods Create and Return in order to provide a strategy to achieve

  • Create is used to create class instances related
  • Return to a subject that has been put back into the pool completely used, including a reset state, and whether the object can be returned to the pool

    /// <summary>
    /// Represents a policy for managing pooled objects.
    /// </summary>
    /// <typeparam name="T">The type of object which is being pooled.</typeparam>
    public interface IPooledObjectPolicy<T>
    {
        /// <summary>
        /// Create a <typeparamref name="T"/>.
        /// </summary>
        /// <returns>The <typeparamref name="T"/> which was created.</returns>
        T Create();

        /// <summary>
        /// Runs some processing when an object was returned to the pool. Can be used to reset the state of an object and indicate if the object should be returned to the pool.
        /// </summary>
        /// <param name="obj">The object to return to the pool.</param>
        /// <returns><code>true</code> if the object should be returned to the pool. <code>false</code> if it's not possible/desirable for the pool to keep the object.</returns>
        bool Return(T obj);
    }

PooledObjectPolicy is a generic abstract class, and implements IPooledObjectPolicy, provides two abstract methods outside


    public abstract class PooledObjectPolicy<T> : IPooledObjectPolicy<T>
    {
        public abstract T Create();

        public abstract bool Return(T obj);
    }

Implementation Mechanism

DefaultObjectPool

DefaultObjectPool realized ObjectPool, Interlocked.CompareExchange (ref _firstItem, null , item) _firstItem the comparison value and the value of the item, then replace _firstItem with equal null, otherwise it does not operate, replace all previously saved or whether it does not replace returned the value of the _firstItem.

Interlocked operations atoms may be provided as a variable shared by multiple threads.

  • Interlocked.Increment: increment value of the specified variable and stores the result in the form of an atomic operation.
  • Interlocked.Decrement decrement given variables and stores the result in the form of an atomic operation.
  • Interlocked.Add atomic operation, the addition of both the two integers a and replace the first integer and
        public override T Get()
        {
            var item = _firstItem;
            if (item == null || Interlocked.CompareExchange(ref _firstItem, null, item) != item)
            {
                var items = _items;
                for (var i = 0; i < items.Length; i++)
                {
                    item = items[i].Element;
                    if (item != null && Interlocked.CompareExchange(ref items[i].Element, null, item) == item)
                    {
                        return item;
                    }
                }

                item = Create();
            }

            return item;
        }

        public override void Return(T obj)
        {
            if (_isDefaultPolicy || (_fastPolicy?.Return(obj) ?? _policy.Return(obj)))
            {
                if (_firstItem != null || Interlocked.CompareExchange(ref _firstItem, obj, null) != null)
                {
                    var items = _items;
                    for (var i = 0; i < items.Length && Interlocked.CompareExchange(ref items[i].Element, obj, null) != null; ++i)
                    {
                    }
                }
            }
        }

DefaultObjectPoolProvider

DefaultObjectPoolProvider rewritten in Crearte ObjectPoolProvider method,
setting the default maximum number of objects that can only be used (twice the CPU processor) default Environment.ProcessorCount * 2


    /// <summary>
    /// The default <see cref="ObjectPoolProvider"/>.
    /// </summary>
    public class DefaultObjectPoolProvider : ObjectPoolProvider
    {
        /// <summary>
        /// The maximum number of objects to retain in the pool.
        /// </summary>
        public int MaximumRetained { get; set; } = Environment.ProcessorCount * 2;

        /// <inheritdoc/>
        public override ObjectPool<T> Create<T>(IPooledObjectPolicy<T> policy)
        {
            if (policy == null)
            {
                throw new ArgumentNullException(nameof(policy));
            }

            if (typeof(IDisposable).IsAssignableFrom(typeof(T)))
            {
                return new DisposableObjectPool<T>(policy, MaximumRetained);
            }

            return new DefaultObjectPool<T>(policy, MaximumRetained);
        }
    }

DisposableObjectPool

DisposableObjectPool inherited DefaultObjectPool and realized the IDisposable object is used to manually recover


      public void Dispose()
        {
            _isDisposed = true;

            DisposeItem(_firstItem);
            _firstItem = null;

            ObjectWrapper[] items = _items;
            for (var i = 0; i < items.Length; i++)
            {
                DisposeItem(items[i].Element);
                items[i].Element = null;
            }
        }

        private void DisposeItem(T item)
        {
            if (item is IDisposable disposable)
            {
                disposable.Dispose();
            }
        }

LeakTrackingObjectPool

LeakTrackingObjectPool achieve the ObjectPool, which defines a weak He ConditionalWeakTable dictionary reference, and all all the Key Value ConditionalWeakTable <TKey, TValue> weak references are in, and will be recovered in its Key or Key and Value are recovered after automatically disappear from the collection. This means that when you use it to attach some type of a field or property is nothing to worry about the problem of memory leak


    public class LeakTrackingObjectPool<T> : ObjectPool<T> where T : class
    {
        private readonly ConditionalWeakTable<T, Tracker> _trackers = new ConditionalWeakTable<T, Tracker>();
        private readonly ObjectPool<T> _inner;

        public LeakTrackingObjectPool(ObjectPool<T> inner)
        {
            if (inner == null)
            {
                throw new ArgumentNullException(nameof(inner));
            }

            _inner = inner;
        }

        public override T Get()
        {
            var value = _inner.Get();
            _trackers.Add(value, new Tracker());
            return value;
        }

        public override void Return(T obj)
        {
            Tracker tracker;
            if (_trackers.TryGetValue(obj, out tracker))
            {
                _trackers.Remove(obj);
                tracker.Dispose();
            }

            _inner.Return(obj);
        }

        private class Tracker : IDisposable
        {
            private readonly string _stack;
            private bool _disposed;

            public Tracker()
            {
                _stack = Environment.StackTrace;
            }

            public void Dispose()
            {
                _disposed = true;
                GC.SuppressFinalize(this);
            }

            ~Tracker()
            {
                if (!_disposed && !Environment.HasShutdownStarted)
                {
                    Debug.Fail($"{typeof(T).Name} was leaked. Created at: {Environment.NewLine}{_stack}");
                }
            }
        }
    }

reference

https://blog.walterlv.com/post/conditional-weak-table.html

https://www.cnblogs.com/edison0621/p/11747912.html

Guess you like

Origin www.cnblogs.com/yyfh/p/11980645.html