|
一. 内存指标
- Resident Set Size(RSS): 应用实际使用的物理内存+应用依赖的共享库内存
- Unique Set Size(USS): 应用实际使用的物理内存
- Proportional Set Size(PSS): 应用实际使用的物理内存+均摊后的共享库内存 一般来说,游戏开发者最关注的是USS或PSS内存。
二. 分配方式
- Native Mem
- AssetData: Textures,AudioClips,Meshes
- Game Objects & Components: Transforms, etc
- Engine Internals: Managers,Rendering,Physics,etc
- Managed Mem(托管内存)
- Scripts Object (reference type)
- Unity Wrappers objects 注意, Unity Editor下和Runtime的分配方式和内存大小可能都不同, 所以测性能尽可能要在真机上测试
三. Unity Native内存管理
- 有兴趣的可以看一下高川大佬在Unite的演讲 https://www.bilibili.com/video/BV1aJ411t7N6
- 如何优化Native内存
- 关注Scene内存
- 关注Audio
- DSP Buffer(大小设置合适、降低默认声音质量)
- Force to Mono(默认不要双声道)
- Format
- Compression Format选型
- TypeTree。TypeTree 当确定使用的Unity版本和BuildAssetBundle 及 BuildApk 的Unity版本一致时,可以选择关闭TypeTree,避免为了兼容不同版本增加类型数据。关闭这个会提升反序列化效率以及减小内存
- LZ4. LZ4比LZMA压缩包体体积大30%,但速度快10倍,并且分块解压,会减少内存峰值。总之强推LZ4
- Size&Count. 要学会trade-off,AB包Size过大内存占用高,Size过小会让头数据重复内存占用也会高(而且DrawCall可能会爆炸)。官方2019年时的推荐值为1M~2M,现在可以适当加大。而且考虑到其他系统的效率笔者建议在这基础上优先要考虑一个AB存储内容的相关性。
- Unity内部会为了方便访问维护一个红黑树. 该RBTree常驻且在游戏运行前分析完毕,不建议大规模使用
- 不要开w/r: 会在显存内存中各一份
- UI不要开MIPMAP,省33.3..%
- 关注压缩格式。目前来说移动端ASTC最优且品质可调
- 关注图集分配
- 当一个Block连续6次GC没有被访问到会被返回给系统,比较难触发。
- 没有分代的(扫描慢)。从根节点全部扫描一遍
- Mark-Sweep的(会有内存碎片问题)。只是标记清除而没有进行压缩操作
- 保守(不能精确地识别垃圾)的GC。可能会将一个int值识别为地址去标记。精确GC会记录每一次分配的地址,能够知道什么数据是一个地址。
Unity内存最佳实践
- 好的C#程序员会善用Struct(快且不用GC),坏的C#程序员永远class一把梭
- 池中池。构建更加贴合具体模块的内存池(如UI系统、Particle等)
- 少用闭包和匿名函数!底层会转化成一个匿名的class,这个内存很细小很容易在Unity中产生碎片化问题。很容易变成僵尸内存又频繁GC
- 协程一定要有开有关!在MonoBehavior的生命周期中只要协程没被释放,状态就会一直在内存中存着(即使是局部变量)
- 配置表优化。配置表数据请不要全加载进内存!可以采用树形加载等方式
- 单例。想一想你真的需要一个单例吗?
- 可否用静态类、服务选择器代替呢?也许你的”Manager“只需要一个提供静态方法的类
- 可否不提供全局访问只维持单一实例呢?
- 可否使用服务定位器、依赖注入等方式来实现你的框架?
- 如果不是框架级的代码,个人建议禁止使用常驻单例
参考
https://www.bilibili.com/video/BV1aJ411t7N6 |
|