Unity之GC优化

GC优化

前段时间被面试官问到一个问题,“Unity3D游戏开发中如何减少gc”,憋了半天只说出几个C#中的技巧和对象池,感觉没说到点子上,开篇博客整理下,有新的就不定期更新。


unity基于mono运行时,mono运行时的垃圾回收应该和CLR差不多(基于代的引用追踪算法),都是符合.Net规范的,即使是il2cpp也有一套垃圾回收机制在里面(gc.cpp)。gc操作也是一个比较耗时的操作,本以为gc不是性能瓶颈,看了一些博客,想法有所改变,对代码性能的敏感性是需要养成一些习惯的。GC优化究其根源就是减小托管堆的内存分配,分配得越少,就会越晚触发gc,另外还要减少堆分配的频率,内存碎片化也会导致堆空间提前告急。下面是一些gc优化的实践。

  • 托管对象的复用
    复用思想在CLR中的很多地方都有体现,线程池、字符串池都是一种复用的思想,重复使用已经分配的堆空间可以大大减少gc,平时写代码的时候需要对new/copy保持敏感

    1. 利用缓存,停止在循环/频繁调用的方法(update,fixupdate,for循环while循环)中分配堆内存,使用缓存的对象
    2. 利用对象池,需要频繁创建的对象在结束其生命周期后,将其引用保存在相应对象池中便于复用
  • 字符串string相关
    由于字符串是引用类型且字符串的不可变性,大量使用字符串会给gc造成负担

    1. 避免字符串的频繁拼接,以StringBuilder替换
    2. Debug.Log频繁打印会产生很多字符串
    3. Unity的很多api都会产生不同程度的gc垃圾(Tag,Name)
  • 减少堆内存的分配
    1. 避免装箱拆箱,方法提供泛型版本
    2. linq和正则表达式会产生内存垃圾
    3. 协程的嵌套,new WaitForSeconds()缓存
    4. (Unity5.5之前的bug)foreach循环产生垃圾
  • gc的时机和gc的耗时
    1. 非敏感期主动gc
    2. 非托管资源及时释放,如文件流(FileStream),套接字(Socket)等

一些想法

我觉得很多gc优化的策略只需要在【频繁调用/循环/固定游戏周期】时去实践即可,一些【触发式/通知式】的方法没有必要一定为了gc优化而影响了代码的可读性,仅仅为了去挤那么一点牙膏。有时候【可扩展性/可读性/可维护性/稳定性】比gc优化更加重要。

猜你喜欢

转载自blog.csdn.net/aawoe/article/details/79646219