unity之性能优化 对象池之【类对象池】
对象池老生常谈的问题了,对象池即对象的池子。大概描述下:对象池内寄放着废弃的对象,当程序需要某对象去池子里拿,让废弃对象再利用,如果没有则创建;用完之后再将废弃的对象回收。一句话:让内存重复利用的优化方案。
解决痛点:
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 }
TestGameEntry.cs 游戏入口: 脚本挂到我们场景中任何一个物体就可以,我这里是MainCamera
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 }
欢迎大家提出批评建议和指教哈~