System.GC.Collect();
同样,在使用此策略时应谨慎,并注意性能分析器的统计信息,而不能仅仅期待其具有所需的效果。
可重用的对象池
在许多情况下,通过减少创建和销毁的对象数量即可避免生成垃圾。游戏中存在某些类型的对象,例如飞弹,可能会多次反复遇到,但是只有少数对象会同时处于游戏中。在这种情况下,通常可以重用对象,而不是销毁旧对象并替换为新对象。
增量垃圾收集
_增量垃圾收集_将垃圾收集工作分散到多个帧中。
进行增量垃圾收集时,Unity 仍然使用 Boehm–Demers–Weiser 垃圾回收器,但是以增量模式运行。Unity 不会在每次运行时进行完整的垃圾收集,而是将垃圾收集工作负载拆分到多个帧中。因此,不必单次长时间中断程序的执行来让垃圾回收器完成工作,而是进行多次短时间的中断。虽然这不能整体上加快垃圾收集速度,但可以通过将工作负载分布到多个帧来极大减少垃圾收集“尖峰”破坏游戏流畅性的问题。
以下来自 Unity Profiler 的截屏(未启用和已启用增量垃圾收集)说明了增量收集如何减少帧率不稳问题。在这些性能分析跟踪中,帧的浅蓝色部分显示脚本操作所用的时间,黄色部分显示在 Vsync 之前(等待下一帧开始)的帧中剩余时间,而深绿色部分显示垃圾收集花费的时间。
非增量垃圾收集性能分析
如果不进行增量 GC(上图),您会发现尖峰中断了原本平滑的 60fps 帧率。此尖峰使得接受垃圾收集的帧远远超过维持 60FPS 所需的 16 毫秒限制。(实际上,由于垃圾收集,此示例丢弃了多个帧。)
增量垃圾收集性能分析
启用增量垃圾收集(上图)后,同一项目将保持一致的 60fps 帧率,因为垃圾收集操作被分解到若干帧中,只占用每帧的一小段时间(位于黄色 Vsync 跟踪上方的深绿色条纹)。
使用帧中剩余时间的增量垃圾收集
此截屏显示了同一项目,该项目也在启用增量垃圾收集的情况下运行,但是这次每帧脚本操作减少。同样,垃圾收集操作也分解到若干帧中。差异在于这次垃圾收集占用每帧的更多时间,而需要更少的总帧数即可完成。这是因为如果使用 Vsync 或 Application.targetFrameRate,我们可以根据剩余的可用帧时间来调整分配给垃圾收集的时间。这样,我们可以及时运行垃圾收集,不必花时间等待,从而“无代价地”完成垃圾收集。
启用增量垃圾收集
增量垃圾收集当前在以下平台上受支持:
Mac 独立平台播放器
Windows 独立平台播放器
Linux 独立平台播放器
Android
Windows UWP 播放器
Xbox One
Nintendo Switch
Unity Editor
请注意,增量垃圾收集当前在 WebGL 上不受支持。增量垃圾收集需要 .NET 4.x Equivalent 脚本运行时版本。
在受支持的配置中,Unity 在 Player Settings 窗口的“Other settings”区域中将增量垃圾收集作为一个选项。只需选中 Use incremental GC 复选框。
在 Player Settings 中启用增量垃圾收集
此外,如果在项目 Quality 设置中或通过Application.VSync 属性将 VSync Count 设置为 Don’t Sync 之外的选项,或者设置 Application.targetFrameRate 属性,则 Unity 会自动使用给定帧末尾剩余的空闲时间来进行增量垃圾收集。
可以使用 Scripting.GarbageCollector 类,对增量垃圾收集行为进行更精确的控制。例如,如果不想使用 VSync 或目标帧率,则可以自行计算帧结束之前的可用时间,并将该时间提供给垃圾回收器使用。
增量垃圾收集的可能问题
在大多数情况下,增量垃圾收集可以减轻垃圾收集尖峰的问题。但是,在某些情况下,增量垃圾收集在实践中可能没有益处。
增量垃圾收集中断工作时,它将中断标记阶段(该阶段扫描所有托管对象以确定哪些对象仍在使用中以及可以清除哪些对象)。当对象之间的大多数引用在工作片段之间不变时,拆分标记阶段没有问题。对象引用会改变时,必须在下一次迭代中再次扫描那些对象。因此,太多的更改会使增量垃圾回收器不堪重负,并导致标记遍历永远不能完成,因为它总是有更多的工作要做;在这种情况下,垃圾收集会退回到进行完整的非增量收集。
此外,在使用增量垃圾收集时,只要引用发生更改,Unity 就需要生成其他代码(称为写屏障)来通知垃圾收集(因此垃圾收集将知道是否需要重新扫描对象)。更改引用时,这会增加一些开销,可能会对某些托管代码产生明显的性能影响。
尽管如此,大多数典型的 Unity 项目(如果有这样的“典型”Unity 项目)仍可从增量垃圾收集中受益,尤其是项目遭受垃圾收集尖峰时。
始终使用性能分析器来验证您的游戏或程序是否按预期执行。
内存管理是一个微妙而复杂的主题,业界已投入了大量的学术努力。如果有兴趣了解这一主题,memorymanagement.org 将是极好的资源,其中列出了大量出版物和在线文章。如需了解对象池的更多信息,请访问 Wikipedia 页面以及 Sourcemaking.com。
2019–01–17
在 Unity 2018.3 版中添加了在 Mono 和 IL2CPP 脚本后端禁用垃圾收集的功能 NewIn20183
在 Unity 2019.1 中添加了实验性的“增量垃圾收集”功能 NewIn20191
添加了其他平台(PS4、XBox One、Nintendo Switch 和 Unity Editor)上对增量垃圾收集的支持。2019.2 NewIn20192