-
对象池
2019-03-18 18:07:46这个是官方解释 Pool是对象池类,用于对象的存贮、重复使用。 对象池优化是游戏开发中非常重要的...对象池技术能很好解决以上问题,在对象移除消失的时候回收到对象池,需要新对象的时候直接从对象池中取出使用...这个是官方解释
Pool
是对象池类,用于对象的存贮、重复使用。对象池优化是游戏开发中非常重要的优化方式,也是影响游戏性能的重要因素之一。
在游戏中有许多对象在不停的创建与移除,比如角色攻击子弹、特效的创建与移除,NPC的被消灭与刷新等,在创建过程中非常消耗性能,特别是数量多的情况下。
对象池技术能很好解决以上问题,在对象移除消失的时候回收到对象池,需要新对象的时候直接从对象池中取出使用。
优点是减少了实例化对象时的开销,且能让对象反复使用,减少了新内存分配与垃圾回收器运行的机会。
注意:对象移除时并不是立即从内存中抹去,只有认为内存不足时,才会使用垃圾回收机制清空,清空时很耗内存,很可能就会造成卡顿现象。用了对象池后将减少程序的垃圾对象,有效的提高程序的运行速度和稳定性。
合理使用对象池,可以有效减少对象创建的开销,避免频繁的垃圾回收,从而优化游戏流畅度。
对于游戏中使用频繁的对象,例如BOSS对象,玩家模型等,在实例化的过程中,会增加开销,这时候可以采用对象池来减少内存分配与垃圾回收频率,因为回收进对象池的对象不是立即被销毁,而是储存一段时间之后,达到回收内存上限时,才会被回收。
使用对象池回收对象时需要注意的是,在将对象回收进对象池之前先从父节点remove掉(可以调用removeSelf()),另外,不能将已经被销毁(destoryed)的sprite放回对象池
对象池的优点是减少了实例化对象时的开销,且能让对象反复使用,减少了新内存分配与垃圾回收器运行的机会。
-
对象池 和 池对象
2017-01-11 17:11:56 -
对象池 GenericObjectPool 通用对象池
2019-01-28 16:44:51透明代理的使用 在对后端的连接中使用对象池(backend是一个池子 里面是一个域名的连接) 使用需要用到的组件 GenericObjectPool.Config DefaultChannelGroup PoolableObjectFactory BasePoolableObjectFactory ...透明代理的使用 在对后端的连接中使用对象池(backend是一个池子 里面是一个域名的连接)
使用需要用到的组件
- GenericObjectPool.Config
- DefaultChannelGroup
- PoolableObjectFactory BasePoolableObjectFactory
- GenericObjectPool
调用的流程
- 需要重写的函数 makeObject、 validateObject、 destroyObject
- 1、makeObject 创建对象的具体实现
- 2、borrowObject 获取对象池中的对象简单而言就是去LinkedList中获取一个对象,如果不存在的话,要调用构造方法中第一个参数Factory工厂类的makeObject()方法去创建一个对象再获取,获取到对象后要调用validateObject方法判断该对象是否是可用的,如果是可用的才拿去使用。LinkedList容器减一
- 3、returnObject 先调用validateObject方法判断该对象是否是可用的,如果可用则归还到池中,LinkedList容器加一,如果是不可以的则则调用destroyObject方法进行销毁。
避免泄漏
- 产生原因
在一些极端的情况下出现 borrowObject/invalidateObject 没有被调用导致的泄漏问题。 对象泄漏会导致对象池中的对象数量一直上升,达到设置的上限 再调用 borrowObject 就会永远等待或者抛出java.util.NoSuchElementException: Timeout waiting for idle object 异常。
- 解决策略
1. 设置抛弃时间 GenericObjectPool判断一个对象是否泄漏是根据对象最后一次使用或者最后一次borrow的时间进行判断的, 如果超出了预设的值就会被认为是一个泄漏的对象被清理掉(PooledObjectFactory.destroyObject在这一过程中会被调用)。 抛弃时间可以通过 AbandonedConfig.setRemoveAbandonedTimeout 进行设置,时间单位是秒 2. 设置了抛弃时间以后还需要打开泄漏清理才会生效。泄漏判断的开启可以通过两种方式 > 从对象池中获取对象的时候进行清理如果当前对象池中少于2个idle状态的对象或者 active数量>最大对象数-3 的时候, 在borrow对象的时候启动泄漏清理。通过 AbandonedConfig.setRemoveAbandonedOnBorrow 为 true 进行开启。 > 启动定时任务进行清理AbandonedConfig.setRemoveAbandonedOnMaintenance 设置为 true 以后, 在维护任务运行的时候会进行泄漏对象的清理,可以通过 GenericObjectPool.setTimeBetweenEvictionRunsMillis 设置维护任务执行的时间间隔。 - 使用例子 GenericObjectPool<PoolObj> pool = new GenericObjectPool<PoolObj>(new MyPooledObjectFactory(),config); AbandonedConfig abandonedConfig = new AbandonedConfig(); abandonedConfig.setRemoveAbandonedOnMaintenance(true); //在Maintenance的时候检查是否有泄漏 abandonedConfig.setRemoveAbandonedOnBorrow(true); //borrow 的时候检查泄漏 abandonedConfig.setRemoveAbandonedTimeout(10); //如果一个对象borrow之后10秒还没有返还给pool,认为是泄漏的对象 pool.setAbandonedConfig(abandonedConfig); pool.setTimeBetweenEvictionRunsMillis(5000); //5秒运行一次维护任务
透明代理使用demo
- 初始化定义
GenericObjectPool.Config poolConfig = new GenericObjectPool.Config(); poolConfig.maxActive = config.getIntAttribute("maxActive", DEFAULT_POOLCONFIG_MAXACTIVE); poolConfig.maxIdle = config.getIntAttribute("maxIdle", DEFAULT_POOLCONFIG_MAXIDLE); poolConfig.minIdle = config.getIntAttribute("minIdle", DEFAULT_POOLCONFIG_MINIDLE); poolConfig.maxWait = config.getIntAttribute("maxWait", DEFAULT_POOLCONFIG_MAXWAIT); poolConfig.testOnBorrow = true; poolConfig.testOnReturn = true; poolConfig.whenExhaustedAction = GenericObjectPool.WHEN_EXHAUSTED_GROW; port = config.getIntAttribute("port", 80); int idleTimeout = config.getIntAttribute("idleTimeout", DEFAULT_IDLE_TIMEOUT); int connectionTimeout = config.getIntAttribute("connectionTimeout", DEFAULT_CONNECTION_TIMEOUT); int maxResponseLength = config.getIntAttribute("maxResponseLength", DEFAULT_MAX_RESPONSE_LENGTH); channelGroup = new DefaultChannelGroup(); channelPool =new BackendChannelPool(host, port, idleTimeout, connectionTimeout, maxResponseLength, clientSocketChannelFactory, timer, poolConfig, new HostBackendHandlerListener() { @Override public void onExceptionCaught(BackendRequest request, ChannelHandlerContext ctx, ExceptionEvent e) { try { if (request == null) { return; } // 如果是 ClosedChannelException 则无需使pool中的连接失效,因为 // 创建连接的时候已经添加了连接关闭监听器来处理 参看 BackendConnection if (!(e.getCause() instanceof ClosedChannelException) && !(e.getCause() instanceof ConnectException)) { BackendConnection connection = request.getConnection(); channelPool.invalidateObject(connection); } } catch (Exception ex) { LogUtils.error(" invalidate object error ", ex); } } @Override public void onMessageReceived(BackendRequest request, ChannelHandlerContext ctx, MessageEvent e) { } @Override public void onChannelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) { channelGroup.add(ctx.getChannel()); } @Override public void onMessageProcessed(BackendRequest request, ProxyHttpResponse response, boolean closeConnection, ChannelHandlerContext ctx, MessageEvent e) { BackendConnection connection = request.getConnection(); try { if (closeConnection) { if (connection.isOpen()) { channelPool.invalidateObject(connection); } } else { channelPool.returnObject(connection); } } catch (Exception ex) { LogUtils.error(" return object error ", ex); } } },backendExecutor); String detectPath = config.getAttribute("detectPath"); int detectPeriod = config.getIntAttribute("detectPeriod", DEFAULT_DETECT_PERIOD_TIME); this.detectTimes = config.getIntAttribute("detectTimes", DEFAULT_DETECT_TIMES); if (!StringUtils.isBlank(detectPath)) { client = new NettyHttpClient(timer); LogUtils.info(this.getName() + " start schedule backend detect " + host + ":" + port + detectPath); detectExecutor = Executors.newSingleThreadScheduledExecutor(); detectExecutor.scheduleAtFixedRate(new BackendDetectThread(host, port, detectPath, client), detectPeriod, detectPeriod, TimeUnit.SECONDS); } }
- 创建对象
public Object makeObject() throws Exception { if(LogUtils.isTraceEnabled()){ LogUtils.trace("BackendChannelPool makeObject"); } // await*() in I/O thread causes a dead lock or sudden performance drop. // pool.borrowObject() 必须在新的线程中执行 // Configure the client. final ClientBootstrap cb = new ClientBootstrap( clientSocketChannelFactory); final BlockingQueue<BackendRequest> requestQueue = new LinkedBlockingQueue<BackendRequest>(); final ChannelPipelineFactory cpf = new ChannelPipelineFactory() { @Override public ChannelPipeline getPipeline() throws Exception { // Create a default pipeline implementation. final ChannelPipeline pipeline = Channels.pipeline(); pipeline.addLast("timeout", new ProxyIdleStateHandler(timer, 0, 0, idleTimeout, TimeUnit.MILLISECONDS)); pipeline.addLast("decoder", new ProxyHttpResponseDecoder()); pipeline.addLast("aggregator", new ProxyHttpChunkAggregator(maxResponseLength)); final BackendRelayingHandler handler = new BackendRelayingHandler( handlerListener,requestQueue,backendExecutor); final BackendRequestEncoder encoder = new BackendRequestEncoder(requestQueue); pipeline.addLast("encoder", encoder); pipeline.addLast("handler", handler); return pipeline; } }; // Set up the event pipeline factory. cb.setPipelineFactory(cpf); //TODO more option config. cb.setOption("connectTimeoutMillis", connectionTimeout * 1000); ChannelFuture future = cb.connect(new InetSocketAddress(host, port)); if(LogUtils.isDebugEnabled()){ LogUtils.debug("ClientChannelObjectFactory.makeObject ChannelFuture: "+host+":"+port); } future = future.await(); if(future.isCancelled()){ throw new ConnectTimeoutException("request cancelled."); }else if(!future.isSuccess()){ throw new ConnectTimeoutException(future.getCause()); }else{ return new BackendConnection(future.getChannel()); } }
重要优化点
-
connectionPool.setMaxActive(maxActive); connectionPool.setMaxIdle(maxActive); 成对配置
-
设置maxWait connectionPool.setMaxWait(maxWait);
不设置会导致当被依赖的服务由于某些故障(如机器资源被某进程大量占用)而响应极慢时,会有大量线程blocked在borrowObject的逻辑,最终导致resin线程耗尽,服务卡死,用户请求被拒绝
因此需要对maxWait进行设置,且设置的时间不宜过大(高吞吐时仍有可能导致web卡死),也不宜过小(可能导致较多的请求失败)
基本原则: qt<N, 其中q为服务最大吞吐(请求/s), t为设置的maxWait时间(s), N为resin的最大线程数 resin的线程数当前为512, 预估最大吞吐为100, 因此有t<N/q=512/100=5.12 我们将其配置为3s(即3000ms), 从而当该对象池中的对象出现异常时,仍可扛住512/3约为170qps的压力
-
Unity之对象池(单例对象池和泛型对象池)
2015-10-13 13:20:09众所周知,游戏开发中内存和性能一直是影响用户游戏体验的至关重要的两个因素,这次说一说对象池的概念。 对象池意义是将游戏中反复创建销毁的对象进行多次利用,从而避免大量对象的销毁与创建而造成CPU的负担。...众所周知,游戏开发中内存和性能一直是影响用户游戏体验的至关重要的两个因素,这次说一说对象池的概念。
对象池意义是将游戏中反复创建销毁的对象进行多次利用,从而避免大量对象的销毁与创建而造成CPU的负担。缺点是占用了更多的内存,但凡事都没有最好的解决办法,在移动平台上针对游戏的优化基本偏向于牺牲空间换取时间,所以以下两种对象池从本质上是为了回收对象服务的,废话不多说,直接上代码。
using UnityEngine; using System.Collections; using System; using System.Collections.Generic; /// <summary> /// 泛型非单例池,代码简洁,通用性广泛 /// </summary> public class MyPool <T> where T : class { private Action <T> mReset; //重置对象的委托 private Func <T> mNew; //创建新对象的委托 private Stack <T> stack; //存放对象的池子,用List等动态数组也可以,推荐泛型数组 public MyPool (Func <T> mNew, Action <T> mReset = null) { this.mNew = mNew; this.mReset = mReset; stack = new Stack<T> (); } //从池子中获取对象的方法,思路是若池子的数量为0,则调用创建新对象委托创建一个对象返回 //否则从池子中拿出一个对象并返回 public T New () { if (stack.Count == 0) { T t = mNew (); return t; } else { T t = stack.Pop (); if (mReset != null) mReset (t); return t; } } //此方法用于将销毁的对象存入池子 public void Store (T t) { stack.Push (t); } //清空池子 public void Clear () { stack.Clear (); } }
此处推荐一个链接,这个对象池的写法很大程度参照这篇文章的,是一位代码节俭主义牛人写的非常好的文章。而他的使用方式如下:
对于使用池子的类我只是写了最基本的应用,推荐将池子放在一个对象类型的控制类中。至于方法里的扩展根据各位看官的项目内容而定。using UnityEngine; using System.Collections; /// <summary> /// 使用池子的测试类 /// </summary> public class Test : MonoBehaviour { public GameObject ballet; //子弹的预设体,在unity引擎中引用 private MyPool <GameObject> pool; void Start () { pool = new MyPool<GameObject> (NewBallet, Reset); } //实例化新子弹的方法 private GameObject NewBallet () { GameObject g = Instantiate (ballet) as GameObject; //实例化新子弹,实例化过程中需要设置子弹的位置等初始化操作写在这 return g; } //重置对象的方法, private void Reset (GameObject g) { g.transform.position = Vector3.zero; g.transform.rotation = Quaternion.identity; g.SetActive (true); //从池子中取后将物体设为可见,也可用其他方法代替 //...... } //销毁对象的方法 private void Destroy (GameObject g) { g.SetActive (false); //放入池子前将物体设为不可见 pool.Store (g); } }
单例池子的使用方式我就不写了,和上面大同小异,只需多传一个池子对应的string值存在字典即可。晚上继续第二天的一天一算法~using UnityEngine; using System.Collections; using System.Collections.Generic; using System; /// <summary> /// 单例对象池,思路是为每种GameObject建立一个新的池子,并给他 /// 一个字符串存在字典中。 /// </summary> public class SinglePool { #region 单例,此处使类继承MonoBehavior并写成继承MonoBehavior的单例也可以 private static SinglePool instance = null; private SinglePool () { } public static SinglePool GetInstance () { if (instance == null) { instance = new SinglePool (); } return instance; } #endregion #region 变量 private Dictionary <string, List <GameObject>> poolDic; //存放池子的字典,池子用泛型List private Action <GameObject> mReset; //重置对象的委托 private Func <GameObject> mNew; //创建新对象的委托 #endregion #region 方法 //可以让字典存放Object而不是GameObject,适用性更广 //从对应字符串池子中取出对象的方法 public GameObject New (string str, Func <GameObject> mNew, Action <GameObject> mReset = null) { if (poolDic.ContainsKey (str)) { //如果字典存在该字符串,取出该池子 if (poolDic [str].Count > 0) { //如果池子里有对象则取出一个对象并返回 GameObject g = poolDic [str] [0]; poolDic [str].Remove (g); if (mReset != null) mReset (g); return g; } else { //如果池子没有对象则返回一个创建的新对象 return mNew (); } } else { //如果字典不存在该字符串则新创建一个池子,并返回一个创建的新对象 poolDic.Add (str, new List<GameObject> ()); return mNew (); } } //销毁的对象存入池子的方法 public void Store (string str, GameObject g) { if (poolDic.ContainsKey (str)) { poolDic [str].Add (g); } } //销毁对象池的方法 public void DestroyPool (string str) { if (poolDic.ContainsKey (str)) { poolDic.Remove (str); } } #endregion }
-
Unity3D框架学习_对象池和对象池管理
2019-04-14 20:41:04Unity3D框架学习_对象池和对象池管理 目录 1、博客介绍 2、内容 (1)ObjectPoolContainer 对象容器 (2)ObjectPool 单一对象池 (3)PoolManager 对象池管理 3、推送 4、结语 1、博客介绍 本篇博客介绍... -
cocoscreator如何使用对象池-对象池使用详解
2019-06-19 15:19:15使用对象池 在运行时进行节点的创建(cc.instantiate)和销毁(node.destroy)操作是非常耗费性能的,因此我们在比较复杂的场景中,通常只有在场景初始化逻辑(onLoad)中才会进行节点的创建,在切换场景时才会进行节点... -
java对象池
2019-06-17 00:04:58首先介绍下对象池,所谓的对象池就是存储一部分对象到一个池中,需要时就取,不需要时候就释放,其实对象池中维护的是两个SparseArray。 那么对象池有什么用呢,当然是有用的,假设一个场景,在消息处理的时候,比如... -
无敌对象池
2020-08-12 15:39:05此对象池主要针对游戏资源,希望对大家有所帮助! 对象池的使用,例如:缓存消除游戏用的砖块 第一步:初始化池子 ObjectPoolManager.Instance.CreatePool(StringDefine.SpritePoolName, 120, 200, ResManager.... -
对象池模式
2017-09-27 16:17:1431.2 对象池模式 31.2.1 定义和类图 (1)定义:对象池是一种设计模式,它通过管理有限对象复用来共享某些稀少或必须付出昂贵代价的资源。 (2)类图: 31.2.2 对象池的扩展接口 (1)两级对象池方式:... -
python对象池
2018-07-17 16:57:52python对象池 python中,一切皆为对象 id方法的返回值就是对象的内存地址。 is运算符,如果引用的是同一个对象则返回true,x is y类似 id(x) == id(y) 小整数对象池 [-5, 256] 这些小整数被定义在了一个整数... -
小整数对象池,大整数对象池
2017-09-06 11:35:39小整数对象池 (在python内置了) 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池,避免为整数频繁申请和销毁内存空间。 Python对小整数的定义是[-5,257]这些整数对象是提前建立好的,不会... -
Python中小整数对象池和大整数对象池
2017-08-26 21:17:261. 小整数对象池 整数在程序中的使用非常广泛,Python为了优化速度,使用了小整数对象池, 避免为整数频繁申请和销毁内存空间。 Python 对小整数的定义是 [-5, 256] 这些整数对象是提前建立好的,不会被垃圾回收。... -
Unity对象池技术
2017-03-02 14:00:31引入对象池技术,可以将暂时不需要用到游戏对象放在对象池(缓冲区),当需要使用到时再从对象池中提取 这里我简单学习了下关于对象池,现在分享出来,写下自己的对对象池的理解 using UnityEngine; using ... -
android对象池
2016-06-26 12:53:31对象池出现主要是用于避免内存抖动。 android-supportv4中提供了Pools的类实现了对象池。 对象池使用的基本思路是:将用过的对象保存起来,等下一次需要这种对象的时候,再拿出来重复使用,从而在一定程度上减少...
-
visual c++ vc修改文件属性中的创建时间,修改时间,访问时间.zip
-
竞斗云2.0官方固件-备份
-
MySQL开发规范
-
web前端开发规范
-
油烟在线监控系统云平台 油烟环保监测云平台 油烟大数据平台
-
洛谷 P1553 数字反转(升级版) 题解 (字符串C/C++)
-
Git常用命令
-
Leetcode刷题 2021.01.17
-
Linux如何察看一个进程内存的信息.zip
-
大数据Hive on MR/TEZ与hadoop的整合应用
-
grbl 源代码 配置部分翻译中文
-
python办公自动化技巧
-
MTA软件连线使用说明书
-
三、操作Docker容器(持续性纠正更新......)
-
vue创建项目报错:Failed at the chromedriver@2.60.0 install script
-
QT 拼图游戏
-
stm32f103模拟SPI读写W25Q16亲测可用
-
Ubuntu快速一键更换国内更新源(Shell脚本)
-
lvgl LED
-
python容器入门(简单易懂)