对象池老生常谈的问题了,对象池即对象的池子。大概描述下:对象池内寄放着废弃的对象,当程序需要某对象去池子里拿,让废弃对象再利用,如果没有则创建;用完之后再将废弃的对象回收。一句话:让内存重复利用的优化方案。
解决痛点:
1.省去很多内存碎片得问题;
2.节省实例对CPU的消耗;
3.触发垃圾回收(GC)的次数少了。
注意事项:
在场景切换的时候,要销毁整个对象池,避免无意义内存驻留。
废话说完了,先上图:
思路是这样的:
按C键进行取池(去池子里面拿),按D键进行回池(对象回收),常驻数量:设置池中对象数量保持多少不进行释放,没有设置则进行释放。这里设置的是3秒自动释放池中对象。
代码如下:
类对象池:
1 using System; 2 using System.Collections.Generic; 3 4 /// <summary> 5 /// 类对象池 6 /// </summary> 7 public class TestClassObjectPool : IDisposable 8 { 9 /// <summary> 10 /// key 类型哈希码 11 /// </summary> 12 private readonly Dictionary<int, Queue<object>> m_Pools; 13 14 /// <summary> 15 /// 常驻 16 /// </summary> 17 public readonly Dictionary<int, byte> ResidentDic; 18 19 20 #if UNITY_EDITOR 21 /// <summary> 22 /// 编辑器面板监控用的 23 /// </summary> 24 public readonly Dictionary<Type, int> InspectorDic; 25 #endif 26 27 28 public TestClassObjectPool() 29 { 30 m_Pools = new Dictionary<int, Queue<object>>(); 31 ResidentDic = new Dictionary<int, byte>(); 32 #if UNITY_EDITOR 33 InspectorDic = new Dictionary<Type, int>(); 34 #endif 35 } 36 37 /// <summary> 38 /// 取池 39 /// </summary> 40 internal T Dequeue<T>() where T : class, new() 41 { 42 lock (m_Pools) 43 { 44 int key = typeof(T).GetHashCode(); 45 m_Pools.TryGetValue(key, out Queue<object> queueList); 46 if (queueList == null) 47 { 48 queueList = new Queue<object>(); 49 m_Pools[key] = queueList; 50 } 51 else 52 { 53 if (queueList.Count > 0) 54 { 55 UnityEngine.Debug.LogError("池中存在对象 取出一个"); 56 #if UNITY_EDITOR 57 InspectorDic.TryGetValue(typeof(T), out int count); 58 count = count > 0 ? --count : 0; 59 InspectorDic[typeof(T)] = count; 60 #endif 61 return (T)queueList.Dequeue(); 62 } 63 } 64 UnityEngine.Debug.LogError("对象不存在 进行创建"); 65 return new T(); 66 } 67 } 68 69 /// <summary> 70 /// 回池 71 /// </summary> 72 internal void Equeue(object obj) 73 { 74 lock (m_Pools) 75 { 76 int key = obj.GetType().GetHashCode(); 77 m_Pools.TryGetValue(key, out Queue<object> queueList); 78 if (queueList != null) 79 { 80 queueList.Enqueue(obj); 81 #if UNITY_EDITOR 82 InspectorDic.TryGetValue(obj.GetType(), out int count); 83 count++; 84 InspectorDic[obj.GetType()] = count; 85 #endif 86 UnityEngine.Debug.LogError("对象回池了"); 87 } 88 } 89 } 90 91 /// <summary> 92 /// 常驻 93 /// </summary> 94 /// <typeparam name="T"></typeparam> 95 /// <param name="count"></param> 96 internal void SetResident<T>(byte count) where T : class 97 { 98 lock (m_Pools) 99 { 100 int key = typeof(T).GetHashCode(); 101 if (!ResidentDic.ContainsKey(key)) 102 { 103 ResidentDic[key] = count; 104 } 105 } 106 } 107 108 internal void ClearPool() 109 { 110 var vr = m_Pools.GetEnumerator(); 111 while (vr.MoveNext()) 112 { 113 ResidentDic.TryGetValue(vr.Current.Key, out byte count); 114 Queue<object> queue = vr.Current.Value; 115 if (queue.Count > count) 116 { 117 object obj = queue.Dequeue(); 118 #if UNITY_EDITOR 119 InspectorDic.TryGetValue(obj.GetType(), out int insCount); 120 insCount--; 121 InspectorDic[obj.GetType()] = insCount; 122 if (queue.Count == 0) 123 { 124 InspectorDic.Remove(obj.GetType()); 125 } 126 #endif 127 } 128 } 129 } 130 131 public void Dispose() 132 { 133 m_Pools.Clear(); 134 } 135 136 }
对象池管理器:
1 using System;
2 using UnityEngine;
3
4 /// <summary>
5 /// 对象池管理器
6 /// </summary>
7 public class TestPoolManager : IDisposable
8 {
9 /// <summary>
10 /// 下次运行时间
11 /// </summary>
12 private float m_NextRunTime = 0;
13
14 /// <summary>
15 /// 释放间隔
16 /// </summary>
17 private float ClearInterval = 3;
18
19 public TestClassObjectPool ClassPool { get; private set; }
20 public TestPoolManager()
21 {
22 ClassPool = new TestClassObjectPool();
23 m_NextRunTime = Time.time;
24 }
25
26 internal void Init()
27 {
28 }
29
30 public void OnUpdate()
31 {
32 if (Time.time > m_NextRunTime + ClearInterval)
33 {
34 m_NextRunTime = Time.time;
35 ClassPool.ClearPool();
36 }
37 }
38
39 public void Dispose()
40 {
41 ClassPool.Dispose();
42 }
43 }
游戏入口:
1 using UnityEngine;
2
3 public class TestGameEntry : MonoBehaviour
4 {
5 public static TestGameEntry Instance;
6 public static TestTimeManager TimeMgr { get; private set; }
7 public static TestPoolManager PoolMgr { get; private set; }
8
9 private void Awake()
10 {
11 Instance = this;
12 }
13
14 void Start()
15 {
16 InitManagers();
17 Init();
18 }
19
20 void InitManagers()
21 {
22 TimeMgr = new TestTimeManager();
23 PoolMgr = new TestPoolManager();
24 }
25
26 void Init()
27 {
28 TimeMgr.Init();
29 PoolMgr.Init();
30 }
31
32 void Update()
33 {
34 TimeMgr.OnUpdate();
35 PoolMgr.OnUpdate();
36 }
37
38 private void OnDestroy()
39 {
40 TimeMgr.Dispose();
41 PoolMgr.Dispose();
42 }
43 }
编辑器拓展:
1 using UnityEditor;
2 using UnityEngine;
3
4 [CustomEditor(typeof(TestGameEntry))]
5 public class TestPoolManagerInspector : Editor
6 {
7 public override void OnInspectorGUI()
8 {
9 base.OnInspectorGUI();
10 serializedObject.Update();//实时刷新
11 GUILayout.Space(10);
12 GUILayout.BeginVertical("box");
13 GUILayout.BeginHorizontal("box");
14 GUILayout.Space(20);
15 GUILayout.Label("类对象");
16 GUILayout.Label("池中数量");
17 GUILayout.Label("常驻数量");
18 GUILayout.EndHorizontal();
19 if (TestGameEntry.PoolMgr != null)
20 {
21 var insEnumerator = TestGameEntry.PoolMgr.ClassPool.InspectorDic.GetEnumerator();
22 while (insEnumerator.MoveNext())
23 {
24 GUILayout.BeginHorizontal("box");
25 GUILayout.Label(insEnumerator.Current.Key.Name, GUILayout.Width(100));
26 GUILayout.Label(insEnumerator.Current.Value.ToString(), GUILayout.Width(60));
27 TestGameEntry.PoolMgr.ClassPool.ResidentDic.TryGetValue(insEnumerator.Current.Key.GetHashCode(), out byte residentCount);
28 GUILayout.Label(residentCount.ToString(), GUILayout.Width(80));
29 GUILayout.EndHorizontal();
30 }
31 }
32 GUILayout.EndVertical();
33 serializedObject.Update();
34 Repaint();//重绘
35 }
36 }
测试用例:
1 using UnityEngine;
2
3 public class userData
4 {
5 }
6
7 public class userData11
8 {
9 }
10
11 public class TestPool_2 : MonoBehaviour
12 {
13 System.Text.StringBuilder sb = null;
14 userData aa = null;
15 userData11 aa11 = null;
16
17 private void Update()
18 {
19 if (Input.GetKeyDown(KeyCode.C))
20 {
21 Debug.Log("按了C键 --------");
22 sb = TestGameEntry.PoolMgr.ClassPool.Dequeue<System.Text.StringBuilder>(); //new System.Text.StringBuilder();
23 TestGameEntry.PoolMgr.ClassPool.SetResident<System.Text.StringBuilder>(2);
24 sb.Length = 0;
25 sb.Append("111");
26 Debug.Log(sb.ToString());
27
28 aa = TestGameEntry.PoolMgr.ClassPool.Dequeue<userData>();
29 TestGameEntry.PoolMgr.ClassPool.SetResident<userData>(1);
30 aa11 = TestGameEntry.PoolMgr.ClassPool.Dequeue<userData11>();
31 }
32
33 if (Input.GetKeyDown(KeyCode.D))
34 {
35 if (sb == null) return;
36 Debug.Log("按了D键 ========== ");
37 TestGameEntry.PoolMgr.ClassPool.Equeue(sb);
38 TestGameEntry.PoolMgr.ClassPool.Equeue(aa);
39 TestGameEntry.PoolMgr.ClassPool.Equeue(aa11);
40 }
41 }
42 }
欢迎大家提出批评建议和指教哈~