• 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 }

     

    游戏入口:

     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 }

     

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

     

  • 相关阅读:
    LeetCode 32. 最长有效括号(Longest Valid Parentheses)
    LeetCode 141. 环形链表(Linked List Cycle)
    LeetCode 160. 相交链表(Intersection of Two Linked Lists)
    LeetCode 112. 路径总和(Path Sum)
    LeetCode 124. 二叉树中的最大路径和(Binary Tree Maximum Path Sum)
    LightGBM新特性总结
    sql service 事务与锁
    C#泛型实例详解
    C# 中的委托和事件(详解)
    C# DateTime日期格式化
  • 原文地址:https://www.cnblogs.com/zhaolaosan/p/15313521.html
Copyright © 2020-2023  润新知