Unity优化

空间换取时间

Unity内存优化

gc的优化

在游戏运行时数据主要存储在内存中

当游戏数据不需要时 存储当前数据的内存 就可以被回收再次使用

废弃数据所占据的内存 叫做 内存垃圾

GC(垃圾回收机制)是将废弃的内存重新回收并再次使用的过程

GC 是内存管理的一部分 如果废弃的内存过多 则会影响游戏的性能

Unity的内存管理机制

内存管理池:堆内存和堆栈的内存

堆栈stack主要用来存储较小的短暂数据

堆内存heap主要用来存较大的存储时间较长的数据

只要变量处于激活状态(有引用)则其占用的内存被标记为使用状态

一旦变量不再激活 则所占用的内存不在被需要 该内存可以被回收到内存池中再次使用 需要进行内存回收

GC垃圾回事 主要是指对堆上的内存分配和回收untiy会定时对堆内存进行GC操作

回收时

栈可以快速的移除或回收 是实时的

堆的内存不是及时回收的 如果一个变量不在被需要 此内存被标记为可回收

堆的分配

  1. 检测是否有足够的空间单元用来存储数据 如果有空间 则分配
  2. 如果没有足够的空间单元Unity会触发GC释放内存 如果释放后有足够大小的内存单元 则分配
  3. 如果释放内存后依然没有足够的空间单元Unity扩展内存大小然后分配

不管是触发GC 还是扩展堆内存 都是很缓慢的操作 所有尽量减少

GC每次执行的过程

  1. GC 检查内存中每个存储变量
  2. 检测变量的引用是否处于激活状态
  3. 如果变量的引用不是激活状态 添加可回收的标记
  4. 被标记为可回收的变量被移除 其所占内存会被回收到堆内存中

GC的问题

当需要分配新变量时 一旦内存单元不够GC就会频繁的触发

GC的触发 执行流程会消耗大量的时间 尤其是当堆内存中数据量比较大时

导致游戏运行缓慢 帧率运行低的问题

另一方面就是内存的碎片化 内存的分配是取决于变量的大小

当内存被回收后可能会使得内存被分割 形成多个碎片化的单元

即使调用了GC 堆内存总体内存变大了 但是独立的内存单元较小

下次内存分配依然找不到合适的内存单元 又会触发GC 且导致游戏内存越来越大

优化GC方案

  1. 减少GC的运行次数
  2. 减少单次GC的运行时间
  3. 空间GC的运行时间比如场景加载的时候调用

解决方案

  1. 减少new的次数
  2. 字符串拼接使用stringbuilder
  3. list创建时 规定内存大小
  4. foreach迭代器容易导致GC
  5. 使用枚举替代字符串变量
  6. gameobject.tag == “stf” 产生内存垃圾 GameObject.CompareTag(“stf”) 替代
  7. 不要在频繁调用的函数中反复调用进行堆内存分配比如 Update LateUpdate OnTriggerEnter

字符串优化

string是不可变的在C#中 每次对一个string对象进行修改 实际都是创建了一个新字符串

这样会频繁的对内存进行操作 分配内存导致碎片变多 也会导致多次触发GC

  1. stringbuilder在性能上和GC上都有很大的提升 会在长度不够的时候才重新申请内存和赋值
  2. 减少不必要的字符串的创建 缓存字符串
  3. 减少字符串的操作 比如制作UI时 Text中一部分经常改变 一部分不变 比如 “攻击力:100” 拆开成两个控件制作
  4. Debug.log函数 即使输出为空就会创建一个空字符串对象

缓存机制 Pool

频繁的内存操作 会带来极大的消耗和内存碎片的产生

所以所有需要频繁调用情况 都应该使用pool

把需要删除的物体 设置为非激活 然后在需要时再次激活它 复用对象实例

减少Instantiate和Destroy调用

Unity的API一些优化

transform.name

gameObject.tag

都会带来预想不到的内存分配 返回是新实例化的字符串

协程 yield return null 和 yield return 0 返回int值会涉及拆装箱

yield return new WaitForSeconds(1f) 不要每次都new — 缓存

AssetBundle 资源包优化 下载到本地后会生成一个内存镜像 这部分数据也会导致内存增加

而且AB包一般在游戏刚开始运行时加载 很容易导致游戏内的初始化内存占用过高 — 在适当的位置Unload(false)

Resources.Load方法 在需要的时候读取 在资源使用完成或者不在被需要时UnloadAsset UnLoadUnusedAsset 卸载

定时执行

在不影响游戏体验的情况下 主动调用GC操作 System.GC.Collect()

上一篇
下一篇