object pool【上】

来源:互联网 发布:blackstone香烟淘宝店 编辑:程序博客网 时间:2024/06/06 18:04

目的:

在一个固定的POOL里重用对象,而不是单独的分配和释放某一个对象,从而提高性能和内存利用率。

动机:

我们正在实现一个游戏内的视觉效果:英雄施放一个符咒,会在屏幕上产生一个闪烁特效,这时需要一个粒子系统来实现,引擎会生成一个闪光的图形然后播放直到消失。
因为一轮施法会产生数以百计的粒子特效,所以我们需要快速的创建它们,更重要的是,创建和销毁这些粒子不会引起内存碎片化。

碎片的诅咒:

在很多方面单机或手机游戏的编程比传统的PC编程更接近嵌入式编程,内存非常稀缺,用户希望游戏非常稳定,而很少有有效的内存压缩管理器可以使用。在这种情况下,内存碎片是致命的。
内存碎片的意思是堆上的可用空间从一个段大的内存块分离成很多小片。这样即使我们的总内存可用空间很大,但是大的连续的区域却非常的小。举个例子:我们现在有14字节的空闲内存,但是它被一段已经分配的内存所隔开,如果我们想分配一个12字节的对象就会失败,你也看不见屏幕上的特效了亲。
这里写图片描述
即使碎片不是很频繁,它也会降低堆的使用率, 将它变成一个充满洞和裂缝的泡沫塑料,最终毁掉游戏。

两全其美:

因为碎片和分配内存非常慢,游戏非常在意如何和何时管理内存,一个简单的办法是游戏初始化的时候分配一块大的内存,直到游戏结束再释放他们。但是对于某些在运行中需要创建和销毁对象的游戏来说,是非常痛苦的。
对象池是最好的解决办法:预先分配好一大块内存块,游戏运行的时候不要回收,对于用户来说,我们自主的分配和回收这些对象用于我们的核心内容。

模式:

POOL 的定义很简单:维护一个可重用对象的集合,每个对象有一个是否可用的标志可以用来查询,POOL 初始化的时候,它会预先创建整个对象集合,(通常是在一块连续的区域),然后将这些对象设置为不可用的标记。
当你需要一个新的对象,找POOL 要一个。它会找到一个可用对象并标志为“已经使用”然后返回给你。当对象不再需要的时候,会被设置成不可用状态。用这种方法,对象能够自由的创建和销毁而不需要申请内存或者其它资源。

何时使用:
对象池模式广泛用于游戏中,比如说游戏实体或者特效等,但它还可能用于一些不常见的数据比如说播放的声音。
这些情况使用:
频繁创建和销毁对象。
对象大小相似。
在堆上分配对象非常耗时并可能导致内存碎片。
每个对象都封装了资源(数据库 或者 网络连接数据)获取它们代价很大并且可以重用。

铭记于心:

通常你依赖于GC或者new delete操作来帮助你管理内存,但如果使用了对象池,你现在有责任把握它的局局限性:

  • POOL可能会浪费内存在不需要的对象上,你需要根据游戏需要调整POOL 的大小。
  • 在任何时间只有固定数量的对象能够被激活。某种程度上说,这是个好事。通过对象类型将内存分离成一些独立的对象池能够保证这种情况,举例:你正准备生成一个敌人的对象,可是失败了,因为这时一系列爆炸的特效耗掉了你所有内存。
    尽管如此,还是有可能出现你需要重用对象但是它们正在被使用的情况。
    这里有一些解决策略:

  • 完全阻止:这是最普遍的做法,调整POOL 大小,让它永远不会溢出,对于那些比较重要的对象比如说AI的时候这个解决办法是合理的,当你在游戏关卡内将要PK BOSS的时候,你却没有可用的对象来生成,你会束手无策。所以最聪明的做法是保证它肯定不会发生。。
    另外不同的场景对象池所需的大小也不尽相同,你最好根据关卡来做调整。

  • 不要创建:试想你已经面对满屏幕的亮瞎狗眼的特效的时候,用户会觉得你将播放的下一个特效跟让他印象深刻么?

  • 暴力杀死当前对象。。比如说有一个声音的对象池,你打算播放一个新的声音,但是池子满了,你又不想忽略这个声音,因为玩家会察觉到的。玩家正在用一个很牛逼的魔杖施法,有时候刷刷的声音很吊有时候吗的又没声音,他肯定不爽。一个好办法是找到一个比较安静的声音然后悄悄替换它。
    总的来说,只要某个对象的消失比新的对象的不出现更令人不能察觉的话,你可以直接干掉那个老的对象。、

  • 增加内存池大小:如果你的游戏可以更加灵活的掌控内存的话,你可以考虑是否动态的改变POOL 的大小或者是用一个新的更大的POOL覆盖,同样当你发现你的POOL的很多空间多余的时候,你也可以考虑去恢复到POOL之前的大小。

  • 对象的内存占用是固定的。很多对象池用数组存储,对于大小固定的对象来说这样是很好的。但是对于不同类型的对象池来说(比如说某个对象的之类添加了很多的数据),你必须保证每个POOL的插槽有足够的内存去满足那些大的对象,否则,大的对象会挤到下一个对象并产生内存垃圾。
    另一方面,如果对象池的对象大小不一样,也会有很多内存浪费,没一个插槽都需要足够大去容纳最大的那个对象,如果只有少部分大对象,你每次填充一个小的对象就会浪费很多的空间。就像一个傻逼去机场安检,带一个超大箱子装的只是他的钱包和钥匙(当然他如果是去国外购物的话当我没说)。
    如果你发现你干这种傻逼事情,你可以将的POOL分成不同类型的对象池。

  • 重用对象不会自动清理。许多内存管理器清理回收内存后,数据会变成一些魔术数:0xdeadbeef,
    屯吞吞,shutthefuckingup。。等等。这可以用来帮助你查找诡异的未初始化变量的BUG。
    因为对象池不再归memory manager管理了,所以你需多加小心,搞的不好 你用的对象还是它回收前的那个状态,所以在初始化和回收对象的时候必须特别小心。

  • 没用的对象还是在内存中:对象池在有GC的系统中用的不是那么普遍,因为内存管理器通常会帮你处理内存碎片问题,但是它还是可以帮你避免分配和回收内存的消耗,特别是在那些很屎的CPU和很戳的GC的机器上(比如说有手机等移动设备)。
    如果你想对象池和GC和谐相处,要注意潜在冲突。因为对象池并没有释放内存在回收对象的时候,它们仍然在内存中,如果这个时候它有对别的对象的引用,会阻止GC对它们回收利用,所以当对象不再使用的时候,记得清掉所有他的引用。

0 0
原创粉丝点击