03.缓存池
一、C#的内存回收机制
实例化对象时,会在内存中分配空间,删除对象时,只是断开了该对象对内存空间的引用,实际上内存还是被占用的。当我们需要内存时,仍会不断分配内存空间,直到内存中无处可用,才会触发GC。
二、缓存池
1、什么是缓存池
缓存池(Object Pool,对象池)是一种内存 / 对象管理设计模式,核心是提前创建一批常用、创建 / 销毁成本高的对象(如游戏中的子弹、敌人,或程序中的网络连接、线程),将其存入一个 “池” 中;当需要使用时直接从池里获取,使用完毕后不销毁,而是放回池中供后续复用,避免频繁创建和销毁对象的开销。
2、缓存池的作用
降低性能开销
频繁创建 / 销毁对象(尤其是复杂对象,如包含大量组件的游戏物体、需要初始化资源的网络连接)会触发频繁的内存分配与回收(如 Unity 中的 GC 垃圾回收),导致性能波动(如游戏卡顿)。缓存池通过 “复用对象” 减少创建 / 销毁操作,显著降低内存碎片和 GC 压力。稳定程序运行效率
避免 “峰值性能消耗”:例如游戏中瞬间生成大量子弹时,若每次都new
创建,会导致短时间内性能骤降;而从缓存池直接取对象,能保持性能稳定,响应更快。统一管理对象生命周期
缓存池集中管理对象的 “创建、复用、回收”,可统一控制对象的初始化状态(如重置子弹位置、清空数据),避免对象状态混乱;同时便于监控对象数量,防止资源泄漏或过度占用内存(如设置池的最大容量)。
3、典型应用场景
游戏开发:子弹、敌人、特效、UI 弹窗等高频创建 / 销毁的对象;
网络编程:数据库连接池、HTTP 连接池(复用连接,避免频繁建立 TCP 连接);
多线程:线程池(复用线程,避免频繁创建线程的系统开销)。
三、实现一个基础的缓存池
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 缓存池模块
/// </summary>
public class PoolMgr :BaseManager<PoolMgr>
{//缓存池容器public Dictionary<string, List<GameObject>> poolDic = new Dictionary<string, List<GameObject>>();/// <summary>/// 往外拿东西,出池子/// </summary>/// <param name="name"></param>/// <returns></returns>public GameObject GetObj(string name){GameObject obj = null;if (poolDic.ContainsKey(name) && poolDic[name].Count > 0)//有抽屉,抽屉里还有东西{obj = poolDic[name][0];poolDic[name].RemoveAt(0);}else{obj = GameObject.Instantiate(Resources.Load<GameObject>(name));obj.name = name;//把对象名字改成和池子一样的名字}obj.SetActive(true);return obj;}/// <summary>/// 还回暂时不用的东西,进池子/// </summary>public void PushObj(string name,GameObject obj){obj.SetActive(false);if(poolDic.ContainsKey (name))//有抽屉{poolDic[name].Add(obj);}else{poolDic.Add (name,new List<GameObject>{ obj});}}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class DelayPush : MonoBehaviour
{// Start is called before the first frame updatevoid OnEnable(){Invoke("Push", 1);}public void Push(){PoolMgr.GetInstance().PushObj(this.gameObject.name, this.gameObject);}
}
测试脚本:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;public class Test2 : MonoBehaviour
{void Update(){if(Input.GetMouseButtonDown (0)){PoolMgr.GetInstance().GetObj("Cube");}if (Input.GetMouseButtonDown(1)){PoolMgr.GetInstance().GetObj("Sphere");}}
}
四、优化缓存池
由于三中的方法创建出的对象会直接暴露在层级窗口中,会显得没有条理,进行缓存池优化。
1、设置父对象。
2、清空缓存池。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// <summary>
/// 池子中的一列容器
/// </summary>
public class PoolData
{public GameObject fatherObj;//抽屉中对象的父节点public List<GameObject> poolList;//对象的容器public PoolData(GameObject obj,GameObject poolObj){//给抽屉创建父对象,这个父对象是衣柜的子对象fatherObj = new GameObject(obj.name);fatherObj.transform.parent = obj.transform;poolList = new List<GameObject>() {};PushObj(obj);}public void PushObj(GameObject obj){poolList.Add(obj);obj.transform.parent = fatherObj.transform;obj.SetActive(false);}public GameObject GetObj(){GameObject obj = null;obj = poolList[0];poolList.RemoveAt(0);obj.SetActive(true);obj.transform.parent = null;//断开父子关系return obj;}
}/// <summary>
/// 缓存池模块
/// </summary>
public class PoolMgr :BaseManager<PoolMgr>
{//缓存池容器public Dictionary<string, PoolData> poolDic = new Dictionary<string, PoolData>();private GameObject poolObj;/// <summary>/// 往外拿东西,出池子/// </summary>/// <param name="name"></param>/// <returns></returns>public GameObject GetObj(string name){GameObject obj = null;if (poolDic.ContainsKey(name) && poolDic[name].poolList. Count > 0)//有抽屉,抽屉里还有东西{obj = poolDic[name].GetObj();}else{obj = GameObject.Instantiate(Resources.Load<GameObject>(name));obj.name = name;//把对象名字改成和池子一样的名字}obj.SetActive(true);obj.transform.parent = null;//断开父子关系return obj;}/// <summary>/// 还回暂时不用的东西,进池子/// </summary>public void PushObj(string name,GameObject obj){if (poolObj == null)poolObj = new GameObject("Pool");obj.transform.parent = poolObj.transform;//设置父对象为根节点obj.SetActive(false);if(poolDic.ContainsKey (name))//有抽屉{poolDic[name].PushObj(obj);}else{poolDic.Add (name,new PoolData(obj,poolObj));}}public void Clear(){//防止过场景移除后会出现错误//过场景时poolObj会被移除,但内存上还存在引用,此时就会发生错误poolDic.Clear();poolObj = null;}
}