对象池管理系统PoolManager
对象池管理系统PoolManager
背景
在游戏运行过程中,频繁创建和销毁对象会产生较高的性能开销,特别是在使用大量相同类型的对象时,如子弹、特效、敌人、UI小控价等,内存临界时还会自动触发gc,造成卡顿现象。我们引入对象池技术来解决这个问题,对象池通过预先创建一组对象并重复利用它们,从而减少对象的创建和销毁操作,提升内存和性能效率。
为了统一管理游戏中的对象池,在对象池的基础上设计了对象池管理系统PoolManager。此外,该系统能够定期清理长期闲置的对象,进一步优化对象池的内存占用。
使用
创建对象的生成函数
我们需要给PoolManager提供目标对象的生成方法,即 对象生成函数 ,告诉PoolManager这个对象应该怎么生成,以及生成时的初始化、事件通知等其他自定义操作。
下面以一个窗口 Windows 装填多个相同ui控件 Item 的需求举例。
1 | public class Windows : MonoBehaviour |
1 | // 对象池对象需要继承IPoolableObject接口,并实现接口函数 |
申请对象池
向PoolManager申请创建一个对象池,PoolManager会为Item对象生成一个对应的对象池,该对象池全局唯一,处处可向PoolManager获取到
1 | public class Windows : MonoBehaviour |
获取对象
任意地方可以获取对象,GetObject() 获取对象时,对象会自动执行OnActivate()
1 | // 获取10个item |
归还对象
ReturnObject() 归还对象时,对象会自动执行OnDeactivate()
1 | // windows窗口销毁时,归还item对象 |
为了避免忘记归还对象,造成内存泄漏,建议Item对象销毁时主动归还
1 | public class Item : MonoBehaviour, IPoolableObject |
注意事项
- 重复归还对象时会提示warring,说明代码写的有问题,虽然PoolManager处理了重复归还,但就规范来说,应该保证只归还一次对象,开发者应当自行修正归还逻辑。
- 由于对象池内部实现逻辑的抽象程度较高,避免开发者自定义创建 继承对象池 时出现预期外的错误,对象池管理系统虽然允许开发者自定义对象池,但是PoolManager中取消了添加自定义对象池的函数接口。如果开发者对自定义对象池的需求较强烈,可以深入理解对象池管理机制,然后自行在PoolManager中实现 添加继承对象池 的函数接口,或者在具体对象内部维护私有的该继承对象池(不建议)。
内部实现
- PoolManager使用了面向接口编程的方式,使得内部 维护不同类型对象的对象池列表 变得可行且简洁。
1
2
3
4// 声明对象池接口
public interface IObjectPool
{
}1
2
3
4// 具体对象池,继承对象池接口
public class ObjectPool<T> : IObjectPool where T : IPoolableObject
{
}1
2
3
4
5
6public class PoolManager
{
// PoolManager维护 IObjectPool 列表,不需要关心对象池 ObjectPool<T> 的 具体对象类型 T
// 达到不同类型对象的对象池,能共存于同一个字典中的目的
private Dictionary<Type, IObjectPool> _pools;
} - 优化对象池内存占用,传统对象池在归还对象后,并不作另外处理,简而言之就是只增加对象不销毁对象。这样会导致对象池的内存占用越来越大,最终导致内存优化效率还不如不用对象池。PoolManager使用指数衰减算法,以较低频率对所有对象池的所有闲置对象进行扫描,并以一定比例销毁闲置超时的闲置对象,同时保持指定比例的对象留存量。向gc机制标记该闲置对象可清除,以便在手动gc时进行内存归还。使得各对象池的内存占用维持在较低水平。逻辑详见 Yu.ObjectPool.AutoDestroy()
- 为了达到任意位置可以获取对象池或者获取对象的目的,设计成单例模式,并交由GameManager管理声明周期:
1
2
3
4public class PoolManager:BaseSingleTon<PoolManager>,IMonoManager
{
// ……实现 IMonoManager 接口函数
}
评论