【问题标题】:C# how to reduce garbage collectionC#如何减少垃圾收集
【发布时间】:2016-04-03 05:54:27
【问题描述】:

我正在编写一个模拟,它有一个每帧调用的更新循环。在更新函数中,我有数百万个对象要更新,所以看起来像这样。

public void Update(float deltaTime)
{
    Time += deltaTime;
    foreach (SimulationObject obj in objects.ToArray())
        obj.Update(deltaTime);
}

where objects is
List<SimulationObject> objects = new List<SimulationObject>();
and is populated at program initialization time.

您可能会看到 objects.ToArray() 会在每一帧复制这个巨大的列表,然后该副本会被垃圾收集。这给我带来了巨大的性能问题。运行约 2 分钟,收集到的垃圾达到约 2G。由于某些第三方库正在后台异步修改此对象的列表,因此似乎无法删除 ToArray()。

我想知道是否有减少垃圾收集的好方法,避免复制或重用分配的空间?

我是 C# 新手,并尝试搜索答案,但无法找到。如果这是一个重复的帖子,我很抱歉。

【问题讨论】:

标签: c# performance garbage-collection


【解决方案1】:

ToArray() 在这里没有必要,只是浪费时间和内存。使用for 循环。我会从类似的东西开始

for (int i = 0; i < objects.Count; i++) 
{
    SimulationObject obj = null;
    try 
    {
        obj = objects[i];
    }
    catch (ArgumentOutOfRangeException) 
    {
        // something was removed from the collection
        // we reached the end of the list
        break;
    }

    obj.Update(deltaTime)
}

此代码有意拆分obj = objects[i]obj.Update(deltaTime),以避免在Update() 抛出ArgumentOutOfRangeException 时中断循环。

注意两个事实:

  • 某些对象在添加到 objects 集合后会跳过第一次迭代,这实际上应该不是问题,因为它们的添加在设计上是异步的。
  • 某些对象可能会在从objects 列表中删除后仅更新一次(当然,同时与删除,但结果往往看起来像它发生在“之后”)。根据模拟情况,这种情况可能是不可取的,并且可能需要一些特定的处理。

【讨论】:

  • 这实际上绕过了该异常(同时可能会捕获不同的异常)。这可能就可以了。
  • 哎呀我的错。我认为如果 OP 首先使用线程安全集合或维护两个列表会更好。
  • 如果反向循环,抛出异常的可能性会大大降低。
  • @egrunin objects.Count 在每次迭代时都会被检查,因此循环顺序没有区别,而且在相反的情况下,异常将在第一次迭代时中断循环,而不是在最后一次迭代之后。
【解决方案2】:

没有理由打电话ToArray(),所以你可以使用

public void Update(float deltaTime)
{
    Time += deltaTime;
    foreach (SimulationObject obj in objects)
        obj.Update(deltaTime);
}

【讨论】:

  • 这将抛出一个InvalidOperationException - “集合已修改” - 如果另一个线程在循环遍历集合时修改了集合,就像 OP 所说的那样。
【解决方案3】:

正如有人指出的那样,您不需要使用 toArray() 方法,因为它只会创建当前列表的副本。

如果我理解正确,您这样做是因为您的列表正在由第三方库在后台修改。

我认为在这种情况下你可以安全地使用 Parallel.ForEach 循环,因为你没有修改列表本身,只修改列表中的项目。

      Float Time; // some value
      Float deltaTime; // some value
      Parallel.ForEach(objects, currObject =>
            {
                // do stuff
             Interlocked.increment(ref Time,deltaTime);
             currObject.Update(Time)
            }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-04-30
    • 1970-01-01
    • 1970-01-01
    • 2013-04-01
    • 1970-01-01
    • 2011-01-21
    • 1970-01-01
    相关资源
    最近更新 更多