类对象池,类似对象池,顾名思义就是一定数量的已经创建好的类对象(Object)的集合。当需要创建对象时,先在池子中获取,如果池子中没有符合条件的对象,再进行创建新对象,同样,当对象需要销毁时,不做真正的销毁,而是将其对象SetActive(false),并存入池子中。这样就避免了大量对象的创建销毁,减少了GC,优化了性能。
可以最大限度的减少频繁创建销毁对象,减少GC次数,优化CPU,实现对象的缓存和复用,创建对象的成本比较大,并且创建比较频繁。对象池模式是一种创建型设计模式,它持有一个初始化好的对象的集合,将对象提供给调用者。
运用对象池化技术可以显著地提升性能,尤其是当对象的初始化过程代价较大或者频率较高时。
一定程度上减少了GC的压力。对于实时性要求较高的程序有很大的帮助,比如说 Http 链接的对象池,Redis 对象池,资源加载实体类 等等都使用了对象池
脏对象的问题:所谓的脏对象就是指的是当对象被放回对象池后,还有对象在引用这个对象的内存地址。
脏对象可能带来两个问题:
1)脏对象持有上次使用的引用,导致引用出错。
2)脏对象如果下一次使用时没有做还原,可能导致数据出现问题,最终导致程序逻辑错误。
生命周期的问题:处于对象池中的对象生命周期一般是定期释放,如果无引用并且超时,该对象会被释放。维持大量的对象也是比较占用内存空间的,所以常驻对象数要选择合理的区间较好。
ClassObjectPool.cs 实现代码
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace Myh
- {
- //类池(引用类型的类回收循环利用)
- public class ClassObjectPool:IDisposable
- {
- //类对象 在池中的 常驻数量
- public Dictionary<int, byte> ClassObjectCount
- {
- private set;
- get;
- }
-
- //类对象池缓存字典,key是hash_code,value是该类型的缓存队列
- public Dictionary<int, Queue<object>> m_dicClassObjectPool;
-
-
- #if UNITY_EDITOR
- //在unity面板中显示的信息(类型:数量?)
- public Dictionary
int> DicInspector = new Dictionaryint>(); - #endif
-
- public ClassObjectPool()
- {
- ClassObjectCount = new Dictionary<int, byte>();
- m_dicClassObjectPool = new Dictionary<int, Queue<object>>();
- }
-
- #region 设置类常驻数量
- //设置类常驻数量
- public void SetResideCount<T>(byte count) where T : class
- {
- //得到该类型的hashCode
- int key = typeof(T).GetHashCode();
- ClassObjectCount[key] = count;
- }
- #endregion
-
- //取出一个队列中的对象
- //模板类型约束,约束是类 或者是 结构体(class 和 struct都可以new,但是不能同时约束class和struct,第二个约束只能约束可以new)
- public T Dequeue<T>() where T : class,new()
- {
- //只能把引用类型的变量当成锁
- lock (m_dicClassObjectPool)
- {
- //先找到这个类的哈希(先算出这个类的哈希码)
- int key = typeof(T).GetHashCode();
-
- Queue<object> queue = null;
-
- //尝试取出这个类的缓存队列
- m_dicClassObjectPool.TryGetValue(key, out queue);
-
- //如果队列是空,说明没缓存过,new一个队列
- if (null == queue)
- {
- queue = new Queue<object>();
- m_dicClassObjectPool[key] = queue;
- }
-
- //如果队列里有缓存,取出
- if (queue.Count > 0)
- {
- object obj = queue.Dequeue();
- #if UNITY_EDITOR
- Type t = obj.GetType();
- if (DicInspector.ContainsKey(t))
- {
- DicInspector[t]--;
- }
- else
- {
- DicInspector[t] = 0;
- }
- #endif
- //把刚刚从队列中取出的返回
- return (T)obj;
- }
- else
- {
- //如果队列无缓存,实例化一个
- return new T();
- }
- }
- }
-
- //Enqueue 入队,把使用结束的类对象存入缓存中
- //对象回收
- public void Enqueue(object obj)
- {
- //使用 对象池map m_dicClassObjectPool 作为锁
- lock (m_dicClassObjectPool)
- {
- int key = obj.GetType().GetHashCode();
-
- Queue<object> queue = null;
-
- //取出该类型的队列
- m_dicClassObjectPool.TryGetValue(key,out queue);
-
- #if UNITY_EDITOR
- Type t = obj.GetType();
- if (DicInspector.ContainsKey(t))
- {
- DicInspector[t]++;
- }
- else
- {
- DicInspector[t] = 1;
- }
- #endif
-
- //如果不是从类对象池取出来的,视为无效,不可存入缓存中
- if (null != queue)
- {
- queue.Enqueue(obj);
- }
- }
- }
-
-
- //释放对象池
- public void Release()
- {
- lock (m_dicClassObjectPool)
- {
- //队列的数量
- int queueCount = 0;
-
- //定义迭代器
- IEnumerator
int, Queue<object>>> iter = m_dicClassObjectPool.GetEnumerator(); -
- for (; iter.MoveNext();)
- {
- //hash_code
- int key = iter.Current.Key;
-
- //拿到type对应的队列
- Queue<object> queue = m_dicClassObjectPool[key];
-
- #if UNITY_EDITOR
- Type t = null;
- #endif
- queueCount = queue.Count;
-
- //用户释放的时候 判断
- byte resideCount = 0;
- ClassObjectCount.TryGetValue(key,out resideCount);
-
- //队列内的数量>持久化的数量才释放队列内的缓存
- //队列内要保留 =resideCount 个的数量
- while (queueCount > resideCount)
- {
- //队列中有可释放的对象
- --queueCount;
-
- //从队列中取出一个,这个对象没有任何引用,就变成了野指针 等待GC回收
- object obj = queue.Dequeue();
- #if UNITY_EDITOR
- t = obj.GetType();
- DicInspector[t]--;
- #endif
- }
-
- //队列为空,从字典移除
- if (queueCount == 0)
- {
- #if UNITY_EDITOR
- if (null != t)
- {
- DicInspector.Remove(t);
- }
- #endif
- }
- }
-
- //GC 整个项目中,有一处GC即可
- GC.Collect();
- }
- }
-
- public void Dispose()
- {
- m_dicClassObjectPool.Clear();
- }
- }
- }
引用
参考文章: