Unity对象池技术

Unity对象池技术
https://blog.csdn.net/LemonXQ/article/details/77148886

 

顾名思义,对象池是存放对象的缓冲区。用户可以从缓冲区中放入/取出对象。一类对象池存放一类特定的对象。那么对象池有什么用呢?在游戏中,经常会有产生/销毁大量同类游戏对象的需求,比如游戏中源源不断的敌人、频繁刷新的宝箱、乃至一些游戏特效(风、雨等)。如果没有一种比较好的机制来管理这些对象的产生和销毁,而是一昧的Instantiate和Destroy,将使你的游戏性能大大下降,甚至出现卡死、崩溃……

简而言之,就是当需要使用一个对象的时候,直接从该类对象的对象池中取出(SetActive(true)),如果对象池中无可用对象,再进行Instantitate。而当不再需要该对象时,不直接进行Destroy,而是SetActive(false)并将其回收到对象池中。

 

深入理解对象池技术

https://blog.csdn.net/jxw167/article/details/72377411

游戏中通过重新使用固定池中的对象来提高性能和内存使用,而不是单独分配和释放对象。

粒子播放时,可能导致数百颗粒子产生,系统需要能够很快地创建它们, 更重要的是,我们需要确保创建和销毁这些粒子不会导致内存碎片。

移动设备的编程在许多方面比传统的PC编程更接近嵌入式编程, 内存稀缺,用户期望游戏稳定,高效的压缩内存管理器很少可用。 在这种环境中,内存碎片是致命的。    

碎片意味着我们堆中的可用空间被分解成较小的内存块,而不是一个大的开放块。 可用的总内存可能很大,但最大的连续区域可能会很小。 假设我们已经有十四个字节可用,但是它们被分成两个七字节的片段,它们之间带有大量的使用内存。 如果我们尝试分配一个十二字节的对象,我们将失败。 如下所示:

由于碎片,因为分配可能很慢,游戏对于管理内存的时间和方式非常谨慎。 一个简单的解决方案通常是最好的 - 在游戏开始时抓住大块内存,并且不要释放它直到游戏结束。 但是,对于在游戏运行期间需要创建和销毁东西的系统来说,这是一个痛苦。

对象池给了我们两个世界最好的, 对于内存管理员,我们只是在前面分配一大块内存,而不是在游戏中释放它。 对于池的用户,我们可以自由地分配和释放对象到我们心脏的内容。

定义一个维护一个可重用对象集合的池类。 每个对象都支持一个“在使用中”查询来判断当前是否“活着”。 当池初始化时,它将在前面创建整个对象集合(通常在单个连续分配中),并将它们全部初始化为“未使用”状态。    当你想要一个新对象时,查询一个池, 它找到一个可用对象,将其初始化为“正在使用”,并返回。 当对象不再需要时,它被设置回到“不使用”状态。 这样,可以自由地创建和销毁对象,而不需要分配内存或其他资源。
这种模式在游戏中广泛应用于诸如游戏实体和视觉效果之类的明显的东西,但它也用于不太可见的数据结构,例如当前播放的声音。 使用对象池时:

一、您需要频繁地创建和销毁对象。

二、对象的大小相似。

三、堆上分配对象很慢或可能导致内存碎片化。

四、每个对象都封装了一个资源,例如数据库或网络连接,这些资源很昂贵,可以被重用。

你通常依靠一个垃圾收集器或者新的和删除来处理你的内存管理, 通过使用对象池,您说的是“我更好地了解这些字节应该如何处理”。这意味着你可以处理这种模式的局限性。

    需要根据游戏的需要调整对象池的大小, 调整时,当池太小时,通常很明显(没有什么像碰撞引起你的注意), 还要注意池不算太大, 一个较小的池释放可用于其他东西的存放。

    大多数池实现将对象存储在一个对象数组中, 如果所有的对象都是相同的对象,那就行了。 但是,如果要在池中存储不同类型的对象或可能添加字段的子类的实例,则需要确保池中的每个插槽具有足够的可用对象的内存。 否则,一个意想不到的大对象会踩下下一个对象并将其记录在内。

    同时,当您的对象尺寸不同时, 每个插槽需要足够大以容纳最大的对象,如果对象很少,那么每次在该插槽中放一个较小的对象时,都会丢弃内存。 这就像通过机场安全,并为您的钥匙和钱包使用一个巨大的携带大小的行李托盘。    当你发现自己使用了很多内存的时候,可以考虑将对象池分成不同大小的物体 - 不同大小的物品 - 行李箱的大托盘,小容器的小托盘。    大多数内存管理器都有一个调试功能,可以将新分配或释放的内存清除为一些明显的数值,如0xdeadbeef。 这可以帮助您找到由未初始化的变量引起的错误,或者在释放后使用内存。

    由于我们的对象池在重用对象时不再经历内存管理器,所以我们失去了这个安全网,更糟糕的是,用于“新”对象的内存以前拥有完全相同类型的对象。 这使得几乎不可能告诉您是否在创建新对象时忘记初始化某些内容:存储对象的内存可能已经包含了几乎正确的数据。

    因此,请特别注意,初始化池中新对象的代码完全初始化对象,甚至可能花费一点时间添加一个调试功能,以便在对象被回收时清除对象插槽的内存。

    对象池在支持垃圾收集的系统中不常见,因为内存管理器通常会为您处理碎片。 但是池仍然有用,以避免分配和释放的成本,特别是在CPU速度较慢的移动设备和更简单的垃圾收集器上。    如果您使用对象池与垃圾收集器协同工作,请注意潜在的冲突。 由于池不再使用对象时不会重新分配对象,所以它们保留在内存中。 如果它们包含对其他对象的引用,它将阻止收集器再次收回它们。 为避免这种情况,当一个池化对象不再使用时,请清除其它对象所引用的任何引用。
 

对象池的优劣

https://blog.csdn.net/johnlee175/article/details/50826252

对象池的优点: 复用池中对象, 没有分配内存和创建堆中对象的开销, 没有释放内存和销毁堆中对象的开销, 进而减少垃圾收集器的负担, 避免内存抖动; 不必重复初始化对象状态, 对于比较耗时的constructor和finalize来说非常合适;

 

对象池的缺点: 
    (1)现在Java的对象分配操作不比c语言的malloc调用慢, 对于轻中量级的对象, 分配/释放对象的开销可以忽略不计; 
    (2)并发环境中, 多个线程可能(同时)需要获取池中对象, 进而需要在堆数据结构上进行同步或者因为锁竞争而产生阻塞, 这种开销要比创建销毁对象的开销高数百倍; 
    (3)由于池中对象的数量有限, 势必成为一个可伸缩性瓶颈; 
    (4)很难正确的设定对象池的大小, 如果太小则起不到作用, 如果过大, 则占用内存资源高, 可以起一个线程定期扫描分析, 将池压缩到一个合适的尺寸以节约内存,但为了获得不错的分析结果, 在扫描期间可能需要暂停复用以避免干扰(造成效率低下), 或者使用非常复杂的算法策略(增加维护难度); 
    (5)设计和使用对象池容易出错, 设计上需要注意状态同步, 这是个难点, 使用上可能存在忘记归还(就像c语言编程忘记free一样), 重复归还(可能需要做个循环判断一下是否池中存在此对象, 这也是个开销), 归还后仍旧使用对象(可能造成多个线程并发使用一个对象的情况)等问题;

 

对象池有其特定的适用场景: 
    (1)受限的, 不需要可伸缩性的环境(比如移动设备): cpu性能不够强劲, 内存比较紧张, 垃圾收集, 内存抖动会造成比较大的影响, 需要提高内存管理效率, 响应性比吞吐量更为重要; 
    (2)数量受限的资源, 比如数据库连接; (自己写比较容易埋坑, 建议使用成熟的库方案, 比如c3p0) 
    (3)创建成本高昂的对象, 可斟酌是否池化, 比较常见的有线程池, 字节数组池等; (如果有, 则建议使用成熟的库方案, 比如jdk自带的ThreadPoolExecutor, 而不是自己写)

 

android在支持库中提供了池的构件单元–Pools, 其中Pools.SimplePool实现比较简单, 采用固定长度的数组和后进先出的策略, 池中对象的内存不会再被释放, 直到池本身被垃圾回收; Pools.SynchronizedPool仅仅是在Pools.SimplePool基础上加了个同步锁; 
当然也可以使用apache commons pool2类库作为池的构件单元;


  注: 
  响应性是指一个任务单元需要”多快”能处理完; 
  吞吐量是指一定的计算资源(如CPU, 内存, 硬盘容量, IO带宽)和单位时间内, 能完成”多少”任务; 
  可伸缩性是指增加计算资源(如CPU, 内存, 硬盘容量, IO带宽), 程序的吞吐量的能得到多大程度的增加; 
  Example1: 
  并行GC缩短了每次stop-the-world的时间来提供响应性, 但做完一次full-GC的任务量要比串行GC耗费更长的时间(吞吐量降低); 
  Example2: 
  程序分为彼此独立的表现层/业务逻辑层/持久化层会提供可伸缩性, 但由于在不同系统间传递数据, 比对应的未使用分层模型的程序要慢;


以上参考来自于<java并发编程实战>, <effective java>, <java程序性能优化-让你的Java程序更快, 更稳定>, 均反对滥用池化技术;
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值