Unity GC 优化要点

    整理参考:https://unity3d.com/cn/learn/tutorials/topics/performance-optimization/optimizing-garbage-collection-unity-games (只是看了这篇博客记录的笔记)正则表达式

    游戏运行时使用内存来存储数据,当这些数据再也不被使用时,存储这些数据的内存被释放以便于以后这些内存能够被复用。缓存

一 核心概念

    垃圾:存储这些无用数据的内存的术语。函数

    GC(Garbage Collection):使垃圾(无用内存)能够再次使用的过程。性能

    须要优化GC的缘由:大数据

        ①由于GC的触发是有条件触发,当垃圾存储到必定时候,会被动触发回收这些无用内存。若是垃圾内过多,被动触发次数过多,优化

会形成CPU负荷过多,GC是由CPU启动的。spa

        ②每次触发GC,会有明显的卡顿,帧率下降,尤为低端手机。若是游戏在战斗时候发生卡顿,就是很很差的体验了。操作系统

 

二 Unity内存管理

    被释放:当变量超出做用域时,该内存再也不被使用并能够归还给原来的内存池,当内存被归还给原有的内存池里。3d

    被分配:变量在做用域内,分配给他的内存仍然在使用中,咱们称这部份内存已被分配。orm

    栈:Unity能够访问的内存池之一,用于短时间存储小块数据,变量超出做用域时被自动实时释放。

    堆:Unity能够访问的内存池之一,用于长期存储和较大数据块。变量超出做用域时并无被释放,仍是继续保持被分配状态。

    垃圾收集器(garbage collector):识别和释放未使用的堆内存。垃圾收集器按期清理堆。

    栈分配和释放过程:栈分配和释放简单快速。这是由于栈只用于在短期内存储小数据。分配和释放老是以可预测的顺序发生,

并具备可预测的大小。栈的工做方式相似于栈数据类型:这是一个简单的元素集合,这种状况下的内存块,只能以严格的顺序添加和删除元素。这种简单性和

严格性使得它变得很是快速。

    堆分配过程:堆分配比栈分配复杂额多。由于堆能够用来存储长期和短时间数据及各类不一样类型大小的数据。分配和释放并不老是按可预测的顺序

进行且可能须要大小差距巨大的内存块。

    当一个堆内存建立时,将执行如下步骤:

    ①Unity检查堆上是否有足够的空间内存,若是有,为该变量被分配内存。若是没有,Unity触发GC试图释放未使用的堆内存,这个操做可能很慢。

若是GC以后堆内存足够,则该变量被分配内存 。

    ②GC以后堆上仍是没有足够的空闲内存,Unity将向操做系统申请更多内存以扩大堆大小。这个操做可能很慢。堆分配可能会很慢,特别在必须执行GC和扩大堆大小时。

              (GC是个费时的操做,堆上的对象越多,代码中的引用数越多,GC越费时)

 

     GC时的具体的步骤:

      ①垃圾收集器检索堆上的每一个对象、

      ②垃圾收集器搜索全部当前对象引用以肯定堆上的对象是否仍在做用域内

      ③不在做用域内的对象呗标记为删除

      ④删除被标记的对象并将内存返回给堆。

 

  什么时候触发GC:

    堆分配时堆上的可用内存不足时触发GC。

    GC会自动运行。(频率因平台而异)

    手动强制调用GC。

三 GC 的问题

    若是堆上有不少对象和大量的对象引用要检查,则检查全部这些对象的过程可能很慢。这可能致使游戏卡顿或缓慢运行。

    GC在不合时宜的场合被触发。若是CPU在咱们游戏的性能关键部分已经满负荷了,那此时即便是少许的GC额外开销也可能致使

咱们的游戏卡顿或运行缓慢。

    堆碎片,当从堆中分配内存时,会根据必须存储的数据大小从不一样大小的块中的可用空间中获取内存。

当这些内存返回到堆时,堆可能分红不少由分配块分隔的小空闲块.这意味着虽然可用内存总量很高。可是因为

碎片化严重而没法分配一块连续的大内存。这意味着GC被触发或不得不扩大堆大小。

(严重后果:①游戏内存大小会高于实际所须要的大小 ②GC会被频繁的触发)

四 优化GC(减小GC的次数)

      ①尝试在合适时机(loading时),手动触发GC和扩展堆大小以便GC可控。

      ②缓存,将局部函数中的局部引用变量写成公共。

      ③对象池。

      ④清理容器。

      ⑤字符串的建立之类。

      ⑥Debug.Log的引用。

      ⑦注意装箱,装箱会产生垃圾源于底层,当一个值类型变量被装箱时,Unity在堆上建立一个临时的System.Object

来包装值类型变量。一个System.Object是一个引用类型的变量,因此当这个临时对象被处理时会产生垃圾。

      ⑧StartCortine会产生少许垃圾。

        yield return 0;//会产生垃圾,int变量0被装箱。

        =>>> yield return null

        yield return new WairforSeconds(1f);//若是屡次被调用,会产生不少。

        ==>>>WaitForSeconds delay =new WaitForSeconds(1f);//能够事先缓存起来

           yield return delay;

      ⑨Linq和正则表达式在后台有装箱操做而产生垃圾,最好少使用。

      ⑩构建代码以最小化GC的影响

        代码的构建方式可能会影响GC,即便代码中没有堆分配,也可能会增长GC的负担。可能增长GC的负担之一

是要求检查他不应检查的东西。Struct是值类型变量,可是若是包含一个引用类型便利的struct,那么垃圾收集器必须检查整个

结构体。

        另一个增长GC负担的操做是使用没必要要的对象引用,当垃圾收集器搜索堆上对象的引用时,它必须检查代码中的

每一个当前对象的引用。更少的对象引用意味着更少的工做量。

相关文章
相关标签/搜索