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 }

 

欢迎大家提出批评建议和指教哈~

 

posted @ 2022-05-01 15:48  赵不灰  阅读(312)  评论(0编辑  收藏  举报