Unity中的GC
Posted Hello Bug.
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Unity中的GC相关的知识,希望对你有一定的参考价值。
一:前言
GC就是Grabage Collector,当没有任何栈内存所指向的堆内存空间,所有的垃圾将被GC不定期进行回收并且释放无用内存空间,使这些内存可以再次使用,但是如果垃圾过多将影响到GC的处理性能,从而降低整体的程序性能,在实际开发之中,对于垃圾的产生越少越好
对应的方法是GC.Collect,其功能就是强制对所有垃圾进行回收
二:Unity中的内存管理
Unity是自动内存管理,Unity中可以访问两个内存池:栈内存和堆内存,栈用于短期存储的小数据,堆用于较长时间存储的较大的数据块。当创建变量的时候,Unity会从栈或堆中请求一个内存块,只要变量值作用域内(仍可被代码访问),分配给它的内存就会保留。当变量不在作用域范围内了,这块内存就不再需要了,可以被创建它的那个内存池回收。只要变量的引用超出了作用域,栈内存就会被重新分配,而堆的内存不同,堆内存不会重新分配即使变量的引用超出了作用域,垃圾回收器会标识没有使用的堆内存,之后定期清理堆内存
三:堆内存分配步骤
——Unity检测是否有足够的闲置内存单元用来存储数据,如果有,则分配对应大小的内存单元
——如果没有足够的存储单元,Unity会触发垃圾回收(GC)来释放不再被使用的堆内存。这步操作是一步缓慢的操作,如果GC后有足够大小的内存单元,则进行内存分配
——如果GC后并没有足够的内存单元,Unity会扩展堆内存的大小,这步操作也会很缓慢,然后分配对应大小的内存单元给变量
四:为什么要避免产生GC?
GC是一个极其消耗性能的工作,每次都需要遍历整个堆内存,堆内存上的变量或者引用越多其运行的操作会更多,耗费的时间就越长
五:什么情况下会产生GC?
class Person
String name;
int age;
public class JavaDemo
public static void main(String args[])
Person per1 = new Person();
Person per2 = new Person();
per1.name = "liu";
per1.age = 11;
per2.name = "yin";
per2.age = 12;
per2 = per1;
per2.age = 100;
例如上面这段代码,当实例化per1和per2时会分配内存,当将per1的引用给per2时,per2分配的内存就变成了内存垃圾,这时候这段内存就会被被标记为垃圾之后在某个时候自动被释放掉
六:GC何时会被触发?
——堆分配时堆上的可用内存不足时触发GC
——GC会不时的自动运行(频率因平台而异)
——手动强制调用GC.Collect
七:如何避免频繁产生GC造成性能问题?
——减少频繁分配内存
避免在Update、LateUpdate等每帧调用的函数中频繁分配内存
void Update()
List<int> list = new List<int>();
Fun(list);
上面这种写法,每次new都会分配一次内存
private List<int> list = new List<int>();
void Update()
list.Clear();
Fun(list);
上面这种写法只有在容器被创建或者扩容时才会有堆分配,从而减少了垃圾内存的产生
——运用对象池
在运行时大量对象的创建和销毁依然会引起GC问题,用对象池技术可以让对象复用而不是重复的创建和销毁
——字符串
在C#中,String是引用类型,它的值是不可变的,一旦被初始化后就不能改变其内容,频繁的修改字符串建议使用StringBuilder
C#中的String和StringBuilder
——Unity函数的调用
例如GameObject.name或GameObject.tag会返回一个新的字符串,意味着频繁调用会产生内存垃圾,可以使用CompareTag代替
private string playerTag = "Player";
void OnTriggerEnter(Collider other)
bool isPlayer = other.gameObject.tag == playerTag;
上面这种写法每次访问GameObject.tag会产生内存垃圾
private string playerTag = "Player";
void OnTriggerEnter(Collider other)
bool isPlayer = other.gameObject.CompareTag(playerTag);
上面这种写法不会产生内存垃圾
——装箱
值类型转换为引用类型的过程称为装箱,装箱会产生GC
——协程
避免yield return 0,因为会产生GC,因为int类型的0被装箱,而使用yield return null替代则不会产生装箱操作
还比如在协程中避免多次new同一个WaitForSeconds对象
while (!isComplete)
yield return new WaitForSeconds(1f);
上面这种写法每次循环都会分配一次新内存
WaitForSeconds delay = new WaitForSeconds(1f);
while (!isComplete)
yield return delay;
上面这种写法只会分配一次内存
——Linq表达式
LINQ和正则表达式由于在后台会有装箱操作而产生垃圾,在有性能要求的时候最好不使用
以上是关于Unity中的GC的主要内容,如果未能解决你的问题,请参考以下文章
Unity ❉ 使用心得 ☀️| Unity中的 GC及优化 超级全面解析 ☆(ゝω・)v 建议收藏!