unity之性能优化 对象池之【类对象池】
对象池老生常谈的问题了,对象池即对象的池子。大概描述下:对象池内寄放着废弃的对象,当程序需要某对象去池子里拿,让废弃对象再利用,如果没有则创建;用完之后再将废弃的对象回收。一句话:让内存重复利用的优化方案。
解决痛点:
1.省去很多内存碎片得问题;
2.节省实例对CPU的消耗;
3.触发垃圾回收(GC)的次数少了。
注意事项:
在场景切换的时候,要销毁整个对象池,避免无意义内存驻留。
废话说完了,先上图:
思路是这样的:
按C键进行取池(去池子里面拿),按D键进行回池(对象回收),常驻数量:设置池中对象数量保持多少不进行释放,没有设置则进行释放。这里设置的是3秒自动释放池中对象。
代码如下:
类对象池:
1 using System; 2 using System.Collections.Generic; 3 4 ///5 /// 类对象池 6 /// 7 public class TestClassObjectPool : IDisposable 8 { 9 /// 10 /// key 类型哈希码 11 /// 12 private readonly Dictionary<int, Queue<object>> m_Pools; 13 14 /// 15 /// 常驻 16 /// 17 public readonly Dictionary<int, byte> ResidentDic; 18 19 20 #if UNITY_EDITOR 21 /// 22 /// 编辑器面板监控用的 23 /// 24 public readonly Dictionary 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 int>(); 34 #endif 35 } 36 37 /// 38 /// 取池 39 /// 40 internal T Dequeue () 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 /// 70 /// 回池 71 /// 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 /// 92 /// 常驻 93 /// 94 /// 95 /// 96 internal void SetResident (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 ///
5 /// 对象池管理器
6 ///
7 public class TestPoolManager : IDisposable
8 {
9 ///
10 /// 下次运行时间
11 ///
12 private float m_NextRunTime = 0;
13
14 ///
15 /// 释放间隔
16 ///
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(); //new System.Text.StringBuilder();
23 TestGameEntry.PoolMgr.ClassPool.SetResident(2);
24 sb.Length = 0;
25 sb.Append("111");
26 Debug.Log(sb.ToString());
27
28 aa = TestGameEntry.PoolMgr.ClassPool.Dequeue();
29 TestGameEntry.PoolMgr.ClassPool.SetResident(1);
30 aa11 = TestGameEntry.PoolMgr.ClassPool.Dequeue();
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 }
欢迎大家提出批评建议和指教哈~