• Unity3d框架搭建 使用 类对象池技术 优化 C#语言 GC


    一、类对象池概念

    1.类对象池介绍

            类对象池,类似对象池,顾名思义就是一定数量的已经创建好的类对象(Object)的集合。当需要创建对象时,先在池子中获取,如果池子中没有符合条件的对象,再进行创建新对象,同样,当对象需要销毁时,不做真正的销毁,而是将其对象SetActive(false),并存入池子中。这样就避免了大量对象的创建销毁,减少了GC,优化了性能。

    2. 对象池解决什么问题?

            可以最大限度的减少频繁创建销毁对象,减少GC次数,优化CPU,实现对象的缓存和复用,创建对象的成本比较大,并且创建比较频繁。对象池模式是一种创建型设计模式,它持有一个初始化好的对象的集合,将对象提供给调用者。

    3.对象池的优缺点对比

    a.对象池的优点:

    运用对象池化技术可以显著地提升性能,尤其是当对象的初始化过程代价较大或者频率较高时。
    一定程度上减少了GC的压力。对于实时性要求较高的程序有很大的帮助,比如说 Http 链接的对象池,Redis 对象池,资源加载实体类 等等都使用了对象池

    b. 对象池弊端

    脏对象的问题:所谓的脏对象就是指的是当对象被放回对象池后,还有对象在引用这个对象的内存地址。

    脏对象可能带来两个问题:
    1)脏对象持有上次使用的引用,导致引用出错。
    2)脏对象如果下一次使用时没有做还原,可能导致数据出现问题,最终导致程序逻辑错误。
    生命周期的问题:处于对象池中的对象生命周期一般是定期释放,如果无引用并且超时,该对象会被释放。维持大量的对象也是比较占用内存空间的,所以常驻对象数要选择合理的区间较好。

    二、代码实现

    ClassObjectPool.cs 实现代码

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Linq;
    4. using System.Text;
    5. using System.Threading.Tasks;
    6. namespace Myh
    7. {
    8. //类池(引用类型的类回收循环利用)
    9. public class ClassObjectPool:IDisposable
    10. {
    11. //类对象 在池中的 常驻数量
    12. public Dictionary<int, byte> ClassObjectCount
    13. {
    14. private set;
    15. get;
    16. }
    17. //类对象池缓存字典,key是hash_code,value是该类型的缓存队列
    18. public Dictionary<int, Queue<object>> m_dicClassObjectPool;
    19. #if UNITY_EDITOR
    20. //在unity面板中显示的信息(类型:数量?)
    21. public Dictionaryint> DicInspector = new Dictionaryint>();
    22. #endif
    23. public ClassObjectPool()
    24. {
    25. ClassObjectCount = new Dictionary<int, byte>();
    26. m_dicClassObjectPool = new Dictionary<int, Queue<object>>();
    27. }
    28. #region 设置类常驻数量
    29. //设置类常驻数量
    30. public void SetResideCount<T>(byte count) where T : class
    31. {
    32. //得到该类型的hashCode
    33. int key = typeof(T).GetHashCode();
    34. ClassObjectCount[key] = count;
    35. }
    36. #endregion
    37. //取出一个队列中的对象
    38. //模板类型约束,约束是类 或者是 结构体(class 和 struct都可以new,但是不能同时约束class和struct,第二个约束只能约束可以new)
    39. public T Dequeue<T>() where T : class,new()
    40. {
    41. //只能把引用类型的变量当成锁
    42. lock (m_dicClassObjectPool)
    43. {
    44. //先找到这个类的哈希(先算出这个类的哈希码)
    45. int key = typeof(T).GetHashCode();
    46. Queue<object> queue = null;
    47. //尝试取出这个类的缓存队列
    48. m_dicClassObjectPool.TryGetValue(key, out queue);
    49. //如果队列是空,说明没缓存过,new一个队列
    50. if (null == queue)
    51. {
    52. queue = new Queue<object>();
    53. m_dicClassObjectPool[key] = queue;
    54. }
    55. //如果队列里有缓存,取出
    56. if (queue.Count > 0)
    57. {
    58. object obj = queue.Dequeue();
    59. #if UNITY_EDITOR
    60. Type t = obj.GetType();
    61. if (DicInspector.ContainsKey(t))
    62. {
    63. DicInspector[t]--;
    64. }
    65. else
    66. {
    67. DicInspector[t] = 0;
    68. }
    69. #endif
    70. //把刚刚从队列中取出的返回
    71. return (T)obj;
    72. }
    73. else
    74. {
    75. //如果队列无缓存,实例化一个
    76. return new T();
    77. }
    78. }
    79. }
    80. //Enqueue 入队,把使用结束的类对象存入缓存中
    81. //对象回收
    82. public void Enqueue(object obj)
    83. {
    84. //使用 对象池map m_dicClassObjectPool 作为锁
    85. lock (m_dicClassObjectPool)
    86. {
    87. int key = obj.GetType().GetHashCode();
    88. Queue<object> queue = null;
    89. //取出该类型的队列
    90. m_dicClassObjectPool.TryGetValue(key,out queue);
    91. #if UNITY_EDITOR
    92. Type t = obj.GetType();
    93. if (DicInspector.ContainsKey(t))
    94. {
    95. DicInspector[t]++;
    96. }
    97. else
    98. {
    99. DicInspector[t] = 1;
    100. }
    101. #endif
    102. //如果不是从类对象池取出来的,视为无效,不可存入缓存中
    103. if (null != queue)
    104. {
    105. queue.Enqueue(obj);
    106. }
    107. }
    108. }
    109. //释放对象池
    110. public void Release()
    111. {
    112. lock (m_dicClassObjectPool)
    113. {
    114. //队列的数量
    115. int queueCount = 0;
    116. //定义迭代器
    117. IEnumeratorint, Queue<object>>> iter = m_dicClassObjectPool.GetEnumerator();
    118. for (; iter.MoveNext();)
    119. {
    120. //hash_code
    121. int key = iter.Current.Key;
    122. //拿到type对应的队列
    123. Queue<object> queue = m_dicClassObjectPool[key];
    124. #if UNITY_EDITOR
    125. Type t = null;
    126. #endif
    127. queueCount = queue.Count;
    128. //用户释放的时候 判断
    129. byte resideCount = 0;
    130. ClassObjectCount.TryGetValue(key,out resideCount);
    131. //队列内的数量>持久化的数量才释放队列内的缓存
    132. //队列内要保留 =resideCount 个的数量
    133. while (queueCount > resideCount)
    134. {
    135. //队列中有可释放的对象
    136. --queueCount;
    137. //从队列中取出一个,这个对象没有任何引用,就变成了野指针 等待GC回收
    138. object obj = queue.Dequeue();
    139. #if UNITY_EDITOR
    140. t = obj.GetType();
    141. DicInspector[t]--;
    142. #endif
    143. }
    144. //队列为空,从字典移除
    145. if (queueCount == 0)
    146. {
    147. #if UNITY_EDITOR
    148. if (null != t)
    149. {
    150. DicInspector.Remove(t);
    151. }
    152. #endif
    153. }
    154. }
    155. //GC 整个项目中,有一处GC即可
    156. GC.Collect();
    157. }
    158. }
    159. public void Dispose()
    160. {
    161. m_dicClassObjectPool.Clear();
    162. }
    163. }
    164. }

    引用

    参考文章:

    1.对象池的介绍与使用_Dream_bin的博客-CSDN博客_对象池

  • 相关阅读:
    Friedman检验和Nemenyi检验画图代码
    SAP MM的基础
    【博客444】Linux Macvlan
    【web前端】<meta>标签
    web爬虫第三弹 Fiddler抓包工具
    关于pytorch构建神经网络细节的学习记录
    后端接口错误总结
    STM32 寄存器配置笔记——USART配置 打印
    如果没有Google这个靠山,Go 凭什么火?
    C++实现排序 - 01 冒泡、选择、插入和希尔排序
  • 原文地址:https://blog.csdn.net/qq_33531923/article/details/127875653