• 【Unity优化】GC优化策略


    Garbage:内存被“不再使用的数据”占据。

    Garbage collection:使上述内存被重新分配。

    两种类型代码:核心引擎代码、用户代码(托管代码)

    1)核心引擎代码:使用手动内存管理,不使用垃圾回收。

    2)用户代码:使用自动内存管理,不需要知道内存管理细节

    自动内存管理中的垃圾回收

    1)两种内存池:栈(stack)、堆(heap 托管堆)

    2)变量在使用范围内时,存储它的内存被分配(allocated)

    3)变量超出使用范围时,存储它的内存被重新分配(deallocated)

    ① 栈:变量在超出使用范围后,占用内存立即被重新分配

    ② 堆:变量在超出使用范围后,占用内存仍然保持被分配状态

    4)垃圾回收器(garbage collector)周期性地标记、重新分配不被使用的堆内存。

    栈内存细节

    1)存储周期短、小块数据

    2)按照后进后出的方式压栈、出栈

    3)变量在超出使用范围后,内存立即被重新分配

    堆内存细节

    1)推荐存储周期长、大块数据;也可存储周期短、小块数据

    2)值类型变量存储在栈上,其他变量存储在堆上()

    3)堆变量被创建时:

    ① Unity 检查堆中是否有足够内存空间,如果足够,则分配内存;

    ② 如果不够,则触发GC(比较耗时)。如果GC后内存足够,则分配内存;

    ③ 如果GC后内存还不够,则扩充堆内存空间(比较耗时),然后分配内存。

    4)堆内存只能被GC重新分配;GC只作用于堆内存

    GC细节

    1)GC执行步骤:

    ① 检测堆上的每个对象

    ② 搜索所有对象的引用,检测是否在有效范围内

    ③ 不在有效范围内的对象,被标记为待清除

    ④ 被标记的对象被清除,内存被重新分配(返还给堆)

    补充:堆上对象越多、代码中引用的对象越多,GC越耗时。

    2)GC执行时机:

    ① 堆内存没有足够空间分配给新变量

    ② 由目标平台决定的,定期执行

    ③ 手动执行

    补充:第①步可能造成频繁GC

    3)GC引起的问题:

    ① 耗时导致的卡顿

    ② 在不恰当的时机GC,影响游戏表现及降帧

    ③ 堆内存碎片化,占用内存虚高,频繁GC

    GC优化策略

    1)三种方式:

    ① 减少GC执行时间

    ② 减少GC执行频率

    ③ 在恰当时机手动调用GC

    1)三种策略:

    ① 减少堆分配、对象引用数量

    ② 减少堆分配、重新分配频率

    ③ 在恰当时机手动调用GC

    GC优化细节

    1)减少垃圾数量

    ① 使用缓存:避免重复的分配、重新分配

    ② 减少在 Update 这种频繁调用的方法中分配堆内存

    ③ 使用 Clear 清理集合,而不是每次生成

    ④ 对象池

    2)常见的不必要的堆内存分配

    ① String:它是不可变的,每次更改都是创建一个新对象。

    1. 常用字符串应该缓存下来
    2. 频繁更新的text,应该把其中变化和不变的部分分开
    3. 对于频繁变化的字符串,使用 StringBuilder 替代
    4. 移除 Debug.Log()

    ② Unity方法不恰当的调用

    1. 返回数组的方法,会创建新的数组,应在反复使用前缓存
    2. 使用 CompareTag 替代 gameObject.tag
    3. 使用 Input.GetTouch、Input.touchCount 替代 Input.touches
    4. 使用 Physics.SphereCastNonAlloc 替代 Pysiscs.SphereCastAll

    ③ 装箱:把值类型变量当引用类型使用,将创建一个临时 Object 封装值类型变量

    1. Object.Eauals 方法参数是 Object,如果传入 int 或 float,则会产生装箱
    2. String.Format 同上
    3. 就算我们自己代码中避免装箱,插件中也可能发生装箱,应移除这些方法的调用

    ④ Coroutines

    1. StartCoroutine 产生少量垃圾(堆内存分配),因为Unity需要创建实例来维护
    2. 减少在性能敏感处调用 StartCoroutine;避免嵌套调用,可能导致调用延迟
    3. yield return 0 会导致装箱,使用 null 代替
    4. 提前缓存 new WaitForSeconds() 对象,复用
    5. 如果 coroutine 产生太多垃圾,使用 Undate 或消息机制替代 coroutine

    ⑤ 方法引用

    1. 在 Unity 中,匿名方法、命名方法的引用是一个引用类型变量,产生堆分配
    2. 闭包会显著增加内存占用和堆分配,因为捕获了临时变量
    3. 在 gameplay 中避免使用匿名函数、闭包
    4. 避免使用 LINQ、正则表达式,因为都会产生装箱

    3)从代码架构上避免不必要的GC检测:

    1. struct 不要包含引用类型字段,这会导致检测所有字段
    2. 类中减少不必要的引用字段
  • 相关阅读:
    RabbitMQ之六种队列模式
    面试资料
    位掩码的介绍与使用(小白鼠试毒问题)
    递归函数的写法(以strcpy函数为例)
    查找算法简介及实现
    八大排序算法概述及实现
    快速排序算法(一)
    最大(小)堆和堆排序简介
    满二叉树各种节点数目的计算
    LPSTR、LPCSTR、LPWSTR、LPCWSTR、LPTSTR、LPCTSTR的来源及意义
  • 原文地址:https://www.cnblogs.com/hearthstone/p/13357925.html
Copyright © 2020-2023  润新知