记一次内存优化的分享

本文详细介绍了使用ADB shell、DDMS等工具检测和优化Cocos2d-X游戏内存消耗的过程。包括初步检测、进一步分析内存分布、定位问题并优化内存使用,特别关注了纹理资源和Lua堆大小的优化,以及如何减少不必要的Sprite对象来节省内存。
摘要由CSDN通过智能技术生成

尊重原创:http://cn.cocos2d-x.org/tutorial/show?id=2832


一、初略内存消耗检测:

开始主要使用的是adb shell dumpsys meminfo命令(感谢网易王杰同学热心指导),该命令可以准确统计Android进程的内存情况,特别是对于游戏App非常重要的Native Heap。配合游戏逻辑打开和关闭某些功能大概推测。并且把主要占内存的纹理资源和Lua(lua_gc(L,LUA_GCCOUNT,0))堆大小统计输出。

进过多次测试之后发现内存涨到一定高度不会持续高涨,排除泄露的情况。而且纹理占用比不是很高(不到30%)。而且在第一次进战斗后Lua内存占用非常大(20-30M)。分析后发现关卡波次(config_wave)表异常庞大导致,我们的配置是用lua表配置的,而且常驻内存。而config_wave有接近1W行, 单独加载它大约占14M内存,初用Lua不知啊!

除去lua heap的占用,还有剩下接近50%的内存,我一直以来的理解是,作为普通c++对象,是不太可能占用很多内存资源的,所以一直不太理解这个内存分布情况。所以还需要进一步分析。


二、进一步内存检测:

神奇的Google给了我一些指示:Native Heap分析和优化

关键流程是:

1、给系统安装libc_malloc_debug_leak.so:

上面描述的好像非常麻烦,要编译自己的ROM,但是小米手机自带(应该是开发版)这个库,正好找到一部米1。

2、开启libc.debug.malloc功能:

先Rootadb shell:控制台执行adb shell root获取Root权限。然后依次执行:

1
2
3
adbshell setprop libc.debug. malloc  1
adbshell stop
adbshell start

手机会自动重启,然后可以通过getprop检查libc.debug.malloc变量是否被正确设置。

3、修改Android SDK DDMS的配置 ddms.cfg文件(我的是在C:\Users\xingyun\.android目录下面)。添加"native=true",保存后重启DDMS,记得一定要从SDK中启动,Eclipse里的是没用的。会有一个Native Heap的选项卡,连接手机并且保证libc.debug.malloc变量设置正确后在界面选择自己的进程,点击snpshot Current Native Heap Usage,就会显示进程内所有NativeHeap的内存分配情况。

这里有个问题是只有内存分配地址,没有对应函数和符号。有一个Symbol Search Path,但是没什么用(发现是要用冒号隔开...冒号..不是会和Windows的路径冲突吗?),最终放弃让它自己匹配符号。

有地址,可以通过addr2line(是一个可以将指令的地址和可执行映像转换成文件名、函数名和源代码行数的工具)分析地址对应的符号。注意:这个工具需要的地址并不是DDMS dump出来的绝对地址,而是相对于内存so的地址!通过adb shell读取Android系统文件proc/[pid]/maps获取进程库的内存分布,会发现有很多库会有两份,找其中"r-xp"权限(可执行)的那条就可以了。比如我的查询过程是:DDMS里的地址为83071ef4,so在内存的地址是82000000,所以符号在so的地址是01071ef4。最后 addr2line 01071ef4 -e lib***.so -f 查到:

1
2
_ZN7cocos2d9Texture2D15initWithMipmapsEPNS_11_MipmapInfoEiNS0_11PixelFormatEi
/./renderer/CCTexture2D.cpp:672

很明显这是申请纹理时分配的内存。

但是这样分析效率太低。后来发现DDMS可以把结果导出成一个txt文件,如下图:

1.jpg

导出的txt文件里只有so文件名和地址,以及大小信息,所以写了个简单的脚本进行地址符号转换和分类统计。

最终转换出来这样的输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
{total:76.59M,
categorys:[
{name:Texture,total:24.06M,
stacks:[
{tital:15.76M,count:10,size:3.52M,
frames:[
_ZN7cocos2d9Texture2D15initWithMipmapsEPNS_11_MipmapInfoEiNS0_11PixelFormatEi : /./renderer/CCTexture2D.cpp:672
_ZN7cocos2d9Texture2D12initWithDataEPKviNS0_11PixelFormatEiiRKNS_4Size : /./renderer/CCTexture2D.cpp:557
_ZN7cocos2d9Texture2D13initWithImageEPNS_5ImageENS0_11PixelFormat : /./renderer/CCTexture2D.cpp:794
_ZN7cocos2d9Texture2D13initWithImageEPNS_5Image : /./renderer/CCTexture2D.cpp:727
_ZN7cocos2d12TextureCache8addImageERKS : Cache.cpp:346 (discriminator 2)
_Z34lua_cocos2dx_TextureCache_addImageP9lua_Stat : uto/lua_cocos2dx_auto.cpp:67552
]
},
{tital:4.89M,count:9,size:1.00M,
frames:[
_ZN7cocos2d9Texture2D15initWithMipmapsEPNS_11_MipmapInfoEiNS0_11PixelFormatEi : /./renderer/CCTexture2D.cpp:665
_ZN7cocos2d9Texture2D12initWithDataEPKviNS0_11PixelFormatEiiRKNS_4Size : /./renderer/CCTexture2D.cpp:557
_ZN7cocos2d9Texture2D13initWithImageEPNS_5ImageENS0_11PixelFormat : /./renderer/CCTexture2D.cpp:782
_ZN7cocos2d9Texture2D13initWithImageEPNS_5Image : /./renderer/CCTexture2D.cpp:727
_ZN7cocos2d12TextureCache8addImageERKS : Cache.cpp:346 (discriminator 2)
]
},
{tital:2.00M,count:1,size:2.00M,
frames:[
_ZN7cocos2d9Texture2D15initWithMipmapsEPNS_11_MipmapInfoEiNS0_11PixelFormatEi : /./renderer/CCTexture2D.cpp:665
_ZN7cocos2d9Texture2D12initWithDataEPKviNS0_11PixelFormatEiiRKNS_4Size : /./renderer/CCTexture2D.cpp:557
_ZN7cocos2d9Texture2D13initWithImageEPNS_5ImageENS0_11PixelFormat : /./renderer/CCTexture2D.cpp:782
_ZN7cocos2d9Texture2D13initWithImageEPNS_5Image : /./renderer/CCTexture2D.cpp:727
]
},

输出的是json格式,一般编辑器对json都有“收起内容”的支持,比较方便查看。


上图是进入游戏未打开如何UI的内存分布情况,View的分类里有巨大的内存消耗,这里的View主要是一些显示对象,比如Node,Sprite,RichText等等。

仔细查看发现Sprite的数量有8K,每个Sprite大小有1.25K(不包含Sprite所包含的其他堆对象),就仅仅是精灵对象就占用了10M之多。

上述结果都是在游戏刚登陆时候的统计,把游戏主要UI都打开一遍,统计得到所有Sprite有16K也就是说Sprite对象一共占用了20M。到此已经找到了优化方向,首先Sprite数量是绝对有问题,虽然我们几乎所有UI都是单例的,都是假设一个UI 有500个可见Sprite,10个UI也才5K,所以可以推测肯定会有大量未合理使用的Sprite。这里就要说到我们的UI结构了,我们的UI一般是策划和美术同学编辑的,然后交给程序填逻辑,所以应该有很多UI编辑的不合理。

后来让程序同学去查了一下,果然有很多精灵是策划编辑后内容,然后隐藏起来,运行时根据UI状态切换显示状态,这就难怪导致了那么巨量的精灵。进过这一步优化之后,我们的Sprite数量直接降低到了5-6K个,节省了接近15M的内存资源!!

总结
进过两次优化之后,内存占用减少了接近30M。其实如果经验丰富这些问题应该可以在开发阶段避免的,只是第一次用编辑器和lua写UI这么复杂的项目确实避免不了踩这个坑。在整个优化过程中,通过工具精确定位内存分布是非常重要的。其次通过实际内存分布情况和预算进行对比,很容易发现不正常的分布点,找到它们就好解决了。接下来还可以进一步优化,因为一个精灵对象需要占用1.25K的内存,确实有点不太合理,不过这可能涉及到一些对底层的修改。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值