资源内存占用
项目中的资源中,纹理,网格,动画片断,音频占用最多。
一, 纹理
内存占用最多的资源
- 纹理格式
纹理格式是重点,不仅决定内存占用大小,还决定了加载效率。尽可能选择不同平台硬件支持的纹理格式。比如Android平台的ETC,ETC2;iOS平台的PVRTC;WIndow PC上的DXT等。
色阶问题
由于ETC,PVRTC等格式均为有损压缩,因此当纹理色差范围跨度较大时,不可避免的造成不同程序的“阶梯”状的色阶问题。虽然可以使用真彩色RGBA32/ARGB32来解决这个问题,但是会带来很大的内存占用。最好的方案是,减少纹理的色差范围,尽可能使用硬件支持的压缩格式。ETC1不支持透明通道问题
在Android平台中,部分老设备仅支持OpenGL ES 2.0,其纹理格式只支持ETC1而不支持ETC2。而ETC1又不支持透明通道,所以透明纹理无法被压缩,占用过多内存。最好的做法是,将透明通道拆分出来,这样一张透明纹理,拆分为两张非透明纹理,就可以使用ETC1压缩了。在Shader中读取Alpha纹理即可。使用ETC2代替ETC1
ETC2支持透明通道压缩,所以如果不考虑兼容Android上比较老的设备的话,可以使用ETC2压缩格式。其实现在大部分机型都支持ETC2。
纹理尺寸
使用刚刚可以满足显示效果的纹理尺寸,尽可能的减少纹理尺寸。Mipmap
Mipmap旨在降低渲染带宽压力,提升渲染效率。但是开启后纹理内存会变为1.33倍。对于有深度变化的物体,开启此选择,比如角色,场景。UI纹理是不用开启的。Read & Write
纹理资源的“Read & Write”默认是关闭的,只有当需要读写纹理颜色时,才需要开启。开启后纹理内存占用为2倍。所以一定要关闭此选项。
二,网格
有复杂场景的游戏中,网络往往也会占用较高内存。
Normal,Color,Tangent
在网格批渲染时,如果其中一个有其它网络没有的属性,比如Color,则其它所有网络也会被添加这个属性,增加内存占用。根据渲染需求,删除网络中无用的属性。关闭 Read&Write
引擎模块占用
- WebStream
使用WWW来加载或存储AssetBundle时,会造成WebStream开销。WebStream的大小是AssetBundle原始文件大小 + 解压后的数据大小 + DecompressionBuffer(0.5MB)。Unity5中推荐使用WetRequest
或AssetBundle.LoadFromAsyc
进行加载。
- SerializedFile
使用非www的方式加载本地AssetBundle,会有序列化的开销。
托管堆内存占用
由于Unity使用的Mono版本较低,一直存在一个问题,Mono的堆内存一旦分配,就不会还给系统。堆内存的值由内存使用高峰来决定,所以一定要控制内存占用不要出现高峰。
代码中造成内存大量被占用的原因有:
频繁的创建对象
避免在Update, FixUpdate等频繁执行的函数中创建对象。不仅会不断占用内存,而且会加剧GC到来。Log
Log输出会占用大量内存,同CPU消耗也很高。GetComponent
缓存组件,避免频繁获取组件操作。字符串链接
内存占用标准
不超过150M
512M内存的机型可分配内存不超过200M,iPhone4的可分配内存在180M左右。我们将标准设为150M,App运行时还需要额外空间调用系统库等等,所以加起来一般不会超过200M。内存分配
- 纹理 50M
- 网络 20M
- 动画 15M
- 音频 15M
- 堆内存 40M
- 其它 10M
内存泄漏
常见的被认为是内存泄漏的误区为:
- 进出场景前后,内存占用升高,说明有内存泄漏
- 进出场景前后,Profiler中内存回落正常,但Android中的PSS数值并没有完全回落,说明有内存泄漏。
不能简单的以内存没有完全回落来判断内存是否有泄漏,造成内存没有回落的情况有很多,比如资源常驻内存,Mono堆内存只升不降等。
- 检查资源使用
在场景退出后,相关资源是否被完全卸载。如果AssetBundle在加载完后使用了ab.Unload(false)
进行卸载,那么在退出场景时,一定要清除代码中缓存与引用的Asset,只有这样,在Resources.UnloadUnusedAssets
被调用时所有Assets才会从内存清除。
对比同一场景
对比多次加载后的同一场景,之间有什么不同的资源,那以这些差异的资源,很有可能就是上一个场景泄漏的资源。对比不同场景
对比出不同场景加载后的相同资源,很有可能是其它场景泄漏的资源。
检测WebStream,SerializedFile
使用Profiler分析当前场景中是否有上一个场景中遗留下的AssetBundle资源。通过Android PSS / iOS Instrument查看
在两个场景间不停切换,理论上,多次切换同样的场景,如果Profiler中显示的内存回落正常,那么PSS/Instrument的内存数值波动范围也是趋于稳定的,如果出现PSS/Instrument内存持续增长的情况,则需要注意了。
- Unity自身内存泄漏,概率较小
- 第三方插件在使用时出现了内存泄漏。
空闲堆内存
由于Mono问题,堆内存分配后,不会被回收,只增不减。所以一定要避免一次性分配大量内存,尽量使用内存的占用与分配更加平滑。
资源冗余
某一时刻,同一资源在内存中有多份。
AssetBundle打包机制出现问题
同一份资源被重复打包到多个AssetBundle文件中。AssetBundle卸载问题
如果在加载Asset之后调用了ab.Unload(false)
,那么之后再次加载Asset会导致内存中有两份相同的Asset。资源实例化问题
比如,调用renderer.material
会生成新的实例。如果大量的调用,就会造成大量的资源冗余,给资源卸载也造成了很大压力。
https://blog.uwa4d.com/archives/optimzation_memory_1.html
https://blog.uwa4d.com/archives/optimzation_memory_2.html