Unity游戏性能优化_内存

说到程序内存性能优化,老方法论了,须要建立起对进程内存机制的系统认识。主要分为六个部分(从高地址开始):命令行操作与环境变量,栈,堆,未初始化数据段,已初始化数据段,代码段。


图:来源 https://www.geeksforgeeks.org/

其中堆作为内存优化的核心问题,最后介绍,先讲其他部分。

命令行操作与环境变量

略。

代码段

  • 即编译好的代码,一次性被载入的操作指令。
  • 代码段是只读的,以防止程序以外修改指令。
  • 文本段在堆栈的下面,防止堆栈溢出覆盖它。
  • 通常代码段是共享的。

数据段:已初始化

  • 可以理解为编译型静态数据区。
  • 全局的。
  • 已初始化的。
  • 内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,例如全局常量(public const)和已初始化全局变量(public static)。

数据段:未初始化

  • 又称为BBS段。
  • 全局的。
  • 可以理解为运行时静态数据区。比如存储全局运行时只读型变量(public static readonly)和未初始化的全局变量(public static)。

空间有限,数据类型简单,生命周期短,存储机制简单(压栈 / 出栈:后进先出FILO(First In Last Out))。

速度慢,频繁使用容易产生碎片,减小命中率。

Unity的堆内存空间分为两部分,一部分是托管堆(Managed Heap),集成了垃圾回收器(GC机制);另一部分是本机堆(Native Heap),内存回收需要显式明确的调用相应接口。

托管堆

New生成的容器,实例中的各种声明的变量等,有自动GC(Unity 2019.1新功能: 增量式垃圾回收)机制。

一些优化方式:

  • 缓存/对象池
  • List或则Dictionary使用Clear(),减少New()
  • 在Update函数里频繁New对象
  • 降低逻辑执行频率
  • 减少装箱拆箱的类型转换操作
  • 使用更简单的数据类型
  • string的本质是字符数组,+操作开销大,可以考虑StringBuilder
  • coroutine的使用注意:yield return 0/null,new WaitForSeconds()
  • for遍历时,提前定义好循环的总次数
  • 直接把对象作为引用,改为使用对象的索引
  • 在合适的场合使用Struct替代Class

托管堆的GC机制

触发条件:新申请堆内存时,先检测有没现成可分配的内存块,若没有,再GC,如果GC之后仍没有可分配内存,再向操作系统申请(用户态转向内核态)。

所以尽可能不触发GC机制,目前的GC算法肯定是一种综合策略,其中标记整理算法会由于定义的对象,对象的引用数量过多而消耗太多CPU时间。

GC的步骤:1,检查堆中的每一个对象;2,检索每一个当前对象的引用以判断是否可以删除;3,标记可以删除的对象;4,删除被标记的对象,回收内存。

本机堆

Unity自己的内存管理,模型,贴图,音效等原始资源,主动回收通过Destroy,Resources.UnloadAsset(Obj),Resources.UnloadUnusedAssets和AB.Unload(true/false)实现。


图:Unity AssetBundle的内存管理(PS:现在不要用WWW来下载,通过HttpRequest性能更好)

Destroy
对应上图中Instantiate的那部分Objects。除非缓存和对象池的对象,否则我们一般都会使用这个API进行资源卸载,比如UI。

Resources.UnloadAsset(Obj)
卸载Resources.Load(一般只会是logo和splash一类的资源)和AB.Load的Asset备份

Resources.UnloadUnusedAssets
一般在场景切换的时候调用(Unity会隐式调用),并不能释放AB镜像,AB镜像通过AB.Unload(false)释放。

AB.Unload:主动实现的引用计数机制
因为一些大型的游戏对内存管理的要求比较高,如果不做当前场景内的内存清理,很容易内存占用过大,导致系统性能下降,所以需要使用到API:AB.Unload。

而因为有Unload(true)和Unload(false)两种方式,所以这里就有两种回收粒度的策略。

  1. Unload(true):(慎用!!!)同时清理AB镜像和AB.Load出来的Asset备份,需要对AB和AB.Load出来的Asset备份做引用计数。
  2. Unload(false):只清理AB镜像,只需要对AB做引用计数。

当统计的某个资源对象引用计数为0时,即时处理,如果延时处理复杂度就上去了。

PS:降低资源质量,这是有损优化,一般作为最后的手段。

参考

发布者

Loy Wong

A Programmer.