C# 每秒帧数和垃圾收集
在简单的Unity项目中,我可以轻松地在构建中达到1000fps。然而,我倾向于在这些类型的项目中存在GC问题。我的问题是FPS对GC的影响是否很差。我的逻辑是=>脚本运行并分配内存->脚本每帧执行一次->因此,如果fps很高,它是否应该更快地将垃圾累积到非常大的块中C# 每秒帧数和垃圾收集,c#,unity3d,C#,Unity3d,在简单的Unity项目中,我可以轻松地在构建中达到1000fps。然而,我倾向于在这些类型的项目中存在GC问题。我的问题是FPS对GC的影响是否很差。我的逻辑是=>脚本运行并分配内存->脚本每帧执行一次->因此,如果fps很高,它是否应该更快地将垃圾累积到非常大的块中 例如,假设我们有一个简单的脚本,它创建一个列表,用数据填充列表,然后在每一帧销毁该列表。在垃圾收集上花费的时间在很大程度上取决于垃圾达到垃圾收集所需数量的频率,这可以表示为平均帧上产生的垃圾量和每秒运行的帧数的乘积 private
例如,假设我们有一个简单的脚本,它创建一个列表,用数据填充列表,然后在每一帧销毁该列表。在垃圾收集上花费的时间在很大程度上取决于垃圾达到垃圾收集所需数量的频率,这可以表示为平均帧上产生的垃圾量和每秒运行的帧数的乘积
private float previousTransformPositionX;
void Update()
{
float transformPositionX = transform.position.x;
if (transformPositionX != previousTransformPositionX)
{
ExampleGarbageGeneratingFunction(transformPositionX);
previousTransformPositionX = transformPositionX;
}
}
private float timeSinceLastCalled;
private float delay = 1f;
void Update()
{
timeSinceLastCalled += Time.deltaTime;
if (timeSinceLastCalled > delay)
{
ExampleGarbageGeneratingFunction();
timeSinceLastCalled = 0f;
}
}
因此,是的,如果您真的无法减少每帧创建的垃圾量,那么您可以通过每秒运行更少的帧来减少垃圾收集所花费的时间
private float timeSinceLastCalled;
private float delay = 1f;
void Update()
{
timeSinceLastCalled += Time.deltaTime;
if (timeSinceLastCalled > delay)
{
ExampleGarbageGeneratingFunction();
timeSinceLastCalled = 0f;
}
}
这就是为什么:
减少创建的垃圾量
让我们来研究一些有助于我们减少数量的技术
由我们的代码生成的垃圾
缓存
如果我们的代码反复调用导致堆分配的函数
然后丢弃结果,这会产生不必要的垃圾。
相反,我们应该存储对这些对象的引用并重用它们。
这种技术称为缓存。在下面的示例中,代码
每次调用时都会导致堆分配。这是因为一个新的
数组被创建
void OnTriggerEnter(Collider other)
{
Renderer[] allRenderers = FindObjectsOfType<Renderer>();
ExampleFunction(allRenderers);
}
通过一个简单的更改,我们现在可以确保分配函数
仅当transform.position.x的值已更改时调用。我们是
现在只在必要时进行堆分配,而不是在每个
单帧
private float previousTransformPositionX;
void Update()
{
float transformPositionX = transform.position.x;
if (transformPositionX != previousTransformPositionX)
{
ExampleGarbageGeneratingFunction(transformPositionX);
previousTransformPositionX = transformPositionX;
}
}
另一种减少更新中产生的垃圾的技术是使用
计时器。这适用于生成垃圾的代码
它必须定期运行,但不一定每帧都运行。在
在下面的示例代码中,生成垃圾的函数只运行一次
每帧:
void Update()
{
ExampleGarbageGeneratingFunction();
}
在下面的代码中,我们使用计时器来确保函数
每秒生成一次垃圾运行
private float timeSinceLastCalled;
private float delay = 1f;
void Update()
{
timeSinceLastCalled += Time.deltaTime;
if (timeSinceLastCalled > delay)
{
ExampleGarbageGeneratingFunction();
timeSinceLastCalled = 0f;
}
}
当对频繁运行的代码进行这样的小更改时,可以
大大减少产生的垃圾量
private List myList = new List();
void Update()
{
myList.Clear();
PopulateList(myList);
}
清算托收
创建新集合会导致堆上的分配。如果我们发现
我们在代码中不止一次地创建新集合
应缓存对集合的引用,并使用“清除为空”
它的内容,而不是呼吁新的反复
在下面的示例中,每次新的堆分配都会发生
使用
void Update()
{
List myList = new List();
PopulateList(myList);
}
在下面的示例中,仅当
集合已创建,或者必须在后面调整集合的大小
场景。这大大减少了产生的垃圾量
private List myList = new List();
void Update()
{
myList.Clear();
PopulateList(myList);
}
对象池
即使我们减少脚本中的分配,我们可能仍然有
如果我们创建和销毁很多对象,垃圾收集就会出现问题
在运行时。对象池是一种可以减少分配的技术
通过重用对象而不是重复创建
并摧毁它们。对象池在游戏中被广泛使用,并且
最适合我们频繁繁殖和破坏的情况
相似物体;例如,当用枪射击子弹时
方法更新按帧调用一次,您应该使用FixedUpdate为什么每次都需要重新创建此列表?你不能把它留在记忆里吗?您的列表中存储了什么?问题本身是一个虚构的问题,重要的是我们正在处理的脚本经常分配内存和释放内存。假设unity每10秒收集一次垃圾。因此,游戏运行得越快,收集垃圾的有效载荷就越大。这是否意味着,限制游戏fps实际上会使游戏运行更加顺畅,因为GC峰值往往会对性能造成负面影响。这取决于你在内存中做什么以及什么时候做。如果你有更多的fps,我不认为Unity本身会分配更多的内存。如果您想减少GC峰值,请查看他们的新版本,了解有关Unity自动内存管理的更多信息,请查看