紧循环列表副本会产生大量垃圾

时间:2014-08-04 16:17:11

标签: c# windows-phone-8 garbage-collection xamarin monogame

在严格的游戏循环中,我不想为GC收集任何垃圾来收集并导致游戏口吃。在许多更新循环中,我的代码与此非常相似:

List<ModifierBase> activeModsCopy;
lock (lockObj)
{
    activeModsCopy = new List<ModifierBase>(_activeMods);
}
foreach (ModifierBase mod in activeModsCopy)
{
    mod.Update(elapsedGameTime);
}

游戏以60 FPS运行,因此内存分析器报告了我创建的大量这些临时列表,以便在迭代时安全地防止被修改的列表。什么是快速无垃圾方法来实现相同的结果?

编辑:上面的代码在更新循环中运行。更新循环中没有并发,只有1个线程通过它,但其他线程可以添加或删除_activeMods列表中的元素。只有1个进行更新的线程可以访问activeModsCopy,因为它在更新函数中创建。

1 个答案:

答案 0 :(得分:0)

如果您从未删除列表并重复使用该怎么办?

private ModifierBase[] _mods = new ModifierBase[2000]; 
private int _currentModsLength = 0;

如果您希望动态增长,可以使用List<ModifierBase>(2000)

和您的更新:

public void Update()
{
   _currentModsLength = 0;
   // copy over!!
   lock(_activeMods){
      _currentModsLength = _activeMods.Length;
      for(var i = 0; i < _currentModsLength; i++){
         _mods[i] = _activeMods[i];
      }
   }

   for(var k = 0; k < _currentModsLength; k++)
      _mods[k].Update(..);
}

如果您仍然遇到滞后,很可能不是列表复制的原因,而是您创建了许多mod并丢弃它们的事实。

或者,您可以切换列表并使用 ConcurrentBag 。基本上, ConcurrentBag 可以稍微减少线程争用。因此, ConcurrentBag 不是List,而是每个线程都有一个列表,当您以后想要组合列表时,它会将每个包锁定为微秒。

仅使用堆栈绕过GC有不安全的选项:

ModifierBase* fib = stackalloc ModifierBase[_activeMods.Length];
// do copying over

http://msdn.microsoft.com/en-us/library/cx9s2sy4.aspx