本文来自对 https://gpp.tkchu.me/object-pool.html 学习后自己的总结
概念
对象池,及对象的容器,本身是一个对象,内部管理一个包含数个可复用对象的容器。可以方便的取出和回收。池在初始化时就创建整个对象集合(通常为一次连续分配)。在需要时取出,使用结束后回收,轻易地重用对象而不必每次创建销毁时消耗内存和性能。
解决
在Unity中,我们使用对象池主要解决两个问题:
1.减少new的时候,寻址造成的损耗,消耗的原因主要是内存碎片;
2.减少 Object.Instantiate 时内部进行序列化和反序列化的CPU损耗。
应用场景
对象是需要频繁创建和销毁的。
大小相仿。
内存分配缓慢或会导致内存碎片(频繁销毁导致内存中的不连贯)。
封装了数据库和网络连接这样昂贵又可以重用的资源
细节与应对
可能在不需要的资源上浪费资源。要保证池内存适量,太小太大不可取
取用时池中已空。意味着同时只能激活固定数量的对象,在某种程度上这是好事。 不会使先使用对象使用过多内存而造成后面重要对象无内存可用,但我们依然要想办法解决:
1.完全阻止。对于重要的对象,我们增加相应池的大小,无论获取多少,都不溢出。
2.不要再创建,在已有对象已经很引人注目的情况下(满屏的粒子效果),我们可以不出现新的对象
3.强制干掉一个已有对象。如果已有对象的消失要比新对象的出现更不引人察觉
4.增加池的大小。在运行状态下动态增加池的大小,但要考虑增加内存不在需要时是否缩小池
每个对象的大小是否固定。如果是,很好,但是如果要储存不同类型的对象或者子类实例,就需要保证每个位置可以储存下最大对象,这可能造成内存浪费。如果大小差异很大,建议再细分池
设计
首先,定义分配(Allocate)和回收(Recycle)接口
public interface IPool<T>
{
T Allocate();
bool Recycle(T obj);
}
(使用泛型可以更加灵活,更容易重用代码)
接下来,对象池的⼀个重要功能就是缓存,要想实现缓存就要求对象可以在对象池内部进⾏创建。
为了更灵活的创建对象,抽象出一个工厂
public interface IObjectFactory<T>
{
T Create();
}
同时需要计数和数量控制, 以下是全部代码
using System.Collections.Generic;
public abstract class Pool<T> : IPool<T>
{
#region ICountObserverable
/// <summary>
/// Gets the current count.
/// </summary>
/// <value>The current count.</value>
public int CurCount
{
get { return mCacheStack.Count; }
}
#endregion
protected IObjectFactory<T> mFactory;
protected Stack<T> mCacheStack = new Stack<T>();
/// <summary>
/// default is 5
/// </summary>
protected int mMaxCount = 5;
public virtual T Allocate()
{
return mCacheStack.Count == 0
? mFactory.Create()
: mCacheStack.Pop();
}
public abstract bool Recycle(T obj);
}
决策
我习惯使用泛型,使对象与对象池解耦。这样可以有通用重用的对象池类
习惯以工厂类的方式初始化对象