iPad征服内存优化以及因此产生的令人崩溃的崩溃问题

问题描述

iPad1因为内存不足而频繁崩溃。

在1.x版本中,iPad1上是勉强可以运行;在2.x因为新增了很多功能,加入两个UITextField控件,并且有新职业,导致内存上涨了10mb+,iPad1会出现频繁崩溃的情况。

原因分析

iPad2几乎不用关心内存问题,占用个150mb+都不会内存不足(当然,浪费是可耻的,能够优化就尽量优化);iPad1可用内存只有80mb左右,事实上,内存涨到这个数字已经相当危险了,离崩溃就不远了。

所以应该尽可能的进行优化,保证客户端运行时内存消耗在60mb左右,这个是比较理想的数字。对于新游戏或者是小游戏,这个还是比较容易做到的。但是对于征服这样的老游戏移植来说,这就比较困难了。
一方面多年积累的资源量已经到了一个恐怖的数字,另一方面要保证所有的修改都不会影响到既有功能,每一步都是战战兢兢、如履薄冰。(事实上,即便现在,内存优化也没有达到既有目标,但是优化的过程中确实有一些需要分享的东西)

解决方案

1、3dmotion索引优化,这个是最有价值的优化了,3dmotion.ini中的索引数目从原来的80w,减少到15w,减少了10mb+的内存,并且可以减少程序启动时间。

2、贴图格式由dds转换为pvr格式。

   使用pvr图片可以极大的降低内存消耗和cpu消耗。但是由于征服还没有一个完美的系统化使用流程,所以只能屈居第二位。这里我把一些要点总结一下:

   a、贴图要是2的整次幂,并且是正方形,否则无法成功转换,即便成功转换也无法在实机上面运行。

   b、dds(或png)转换为pvr的时候会有一定程度的画质降低。一些大的背景图尤其明显。所以不是所有图片都适合转换(变通的解决方法见e)

   c、对于小的零散图片应该进行拼接,一方面可以提高效率,另一方面也可以使图片符合a点所说的规格。并且图片维护起来也更清晰(资源更新或修改的情况另作考虑)。

   e、拼接工具推荐使用TexturePacker,功能强大,使用方便,主要特色是可以使用一些选项对图片进行柔化处理,这样即便是大的色彩明晰的背景图,转换为pvr也不会显得很丑。使用的时候也有需要注意的地方:1、转换完毕的配置文件要有个脚本转换为c3支持的ani格式。2、有些2d光效图片要注意,美工制作的时候会在图片留一些空白区域,以使多帧图片对齐,这些空白区域会被TexturePacker滤掉,但其实它们是有存在意义的。

   f、pvr图片最大的问题是不通用,只有powerVR的显卡支持。如果我们对于通用客户端的需求大于性能的需求,png其实也是不错的选择。

3、地图索引结构(LayerInfo)精简。这个精简的内存是非常可观的,但是方法比较土,所以列在第三位。

   征服在创建地图的时候会把整张地图的掩码等信息加载进来,如果地图比较大(比如双龙城972*972),那仅仅这一块儿就消耗了14mb内存。把LayerInfo结构体进行精简,能用char的不用int,能两个变量合成一个的就合成一个,这个结构从16字节减少到8字节。看着数字比较小,但是节约了7mb内存。

   其实关于这块儿还可以进一步优化,但是难道稍大,无法尽快应用到版本中:

   a、地图索引表不再维护一个结构体,而是维护一个指针,因为很多情况下LayerInfo的信息是相同的,它们可以共用一个LayerInfo,地图索引表中保存一个指针就够了。这样可以进一步减少约3mb内存。但是客户端的实际情况是,我们不光要读索引表,还会写索引表(比如新增一个npc),这个时候就要多考虑下了。

   b、使用“2.5D地图动态拼接和加载”这个创新。游戏不再一开始加载地图所有cell信息,而是动态加载。需要考虑的是自动寻路的处理。

4、客户端配置优化。这个其实还是相当耗内存的。比如500k的gui.ini会占用近3mb内存。关于这个有一些零碎的优化:

   a、删除不必要的配置文件。比如显示等级帮助提示的配置文件500k,总共加载会消耗3~5mb内存。

   b、使用一部的静态配置文件读取方案的创新。由于无法直接应用于iOS项目(至少windows平台也要应用,那样编辑工具才能正常使用,所以仅仅希望iOS项目使用,真正配置维护等操作不太现实),并且需要对代码进行相对较大的修改,所以征服没有使用。不过据滕远反馈,可以有效降低内存消耗。

   c、部分配置的加载策略修改,比如Itemtype,现在的处理方式是,ItemType按照id分成几十个文件,加载的时候只加载当前段的文件。并且对每一个ItemInfo都会有一个生存周期。如果超期未使用,则释放此ItemInfo。这样基本可以保证只有我们加载物品的时候,这些ItemInfo才会存在,其他的时候ItemType集合是空的。从而可以减少3~5mb内存。

   d、应用同样的思路,gui.ini拆分成每个对话框一个配置文件。只有对话框打开的时候,才会加载这个配置。不过这个并没有减少多少内存(最多1mb,因为本身配置文件也就占用2+mb内存)。

5、未实际应用,但是构想中的优化:

   a、梳理一下Role Player Hero的三层结构,Monster和Npc不再像现在一样统一都是player,可能可以精简掉很多元素。这个改动涉及的范围比较广,并且由于对象数目不会很大,所以我猜想不会降低多少内存消耗。但是梳理的过程中可能会发现很多浪费内存的地方,并且梳理完毕后,对性能也会有一些优化。

   b、所有地图图素精度降低一半(甚至四分之一),这个需要资源重新修改。真正表现效果未知。不过如果在iPad上效果可以接受的话,无论从安装包体积还是内存消耗上,都会大有益处。

   c、模型贴图精度降低。效果同上。并且由于iPad(或iPhone)屏幕相对较小,所以预想中效果应该是可以接受的。不过话又说回来。今天为了照顾iPad1把所有资源精简了,明天是否会为了照顾new iPad的高清模式而把资源再还原?这是个要考虑的问题。

   d、说白了,客户端内存消耗要么是openGL渲染消耗(其实c3也是有值得优化的地方的,尤其是像征服这样资源量比较恐怖的客户端来说,一点点优化,就可能能够节约10mb内存),要么是客户端逻辑消耗。我们能处理的就是一方面资源精简,另一方面修改客户端逻辑避免内存浪费。

纠正措施

      内存优化完毕后,产生了一个模拟器运行正常,但是实机会崩溃的问题,查了4天才调查出结果。在对ItemType进行优化的时候,我对ItemInfo结构体进行了整理和优化,并且使用pack(1)进行1字节对齐。一个s_ItemTypeSet的保存所有ItemInfo的map,原本是在Item类里面的静态成员变量,但是我为了编译方便(否则每次对ItemInfo结构进行调整都要重编译500+个文件),把这个变量改为全局变量。

      这样问题就来了,模拟器一切正常,但是实机上无法开启程序,一开启就瞬间崩溃,连登陆界面都没有显示。虽然我一开始就猜想是全局变量构造的时候出问题了,导致崩溃。但是反复查看代码都没有发现可疑的地方。并且由于在main函数之前就崩溃了,根本没有进入UIApplication的循环,所以一切记录log,崩溃记录等手段都无法正常使用。只能通过不断回退版本修改代码的方式猜想哪里出问题了。但是这么做无异于大海捞针。

      最终解决问题的方式其实是回归原点的方式。我在客户端写的捕获崩溃记录的方式不可用,但是苹果系统的crash log其实是一直可用的,从中完全可以获取到详细信息。但是解析这个log,需要对应的dSYM符号文件。我闲麻烦一直没有使用过这个方式。但是实在无助了,耐着性子解析了crash log,立即就发现问题了。

      原因可能(之所以说可能是,这个涉及到我所不了解的知识层面,所以以下分析仅仅是猜想)是因为我修改ItemInfo使用了pack(1)的1字节对齐,而全局变量的map构造的时候,因为这个内存对齐而崩溃了,可能是因为arm cpu对全局变量的字节对齐有更多的约束。

      解决方法很简单,或者去掉pack(1)的字节对齐,或者把这个全局变量改成单体类。

你可能感兴趣的:(ios,游戏,优化,工具,ipad,Crash)