.NET - 减少阵列创建时的GC争用

时间:2014-11-12 08:53:07

标签: c# .net arrays garbage-collection contention

我有一些代码可以处理大量的数组复制。基本上我的类是一个使用数组作为支持字段的集合,因为我不想冒任何人修改现有集合的风险,大多数操作都涉及在修改集合之前创建集合的副本,因此也复制了支持阵列。

我注意到复制有时会在可接受的范围内变慢,但我担心当应用程序按比例放大并开始使用更多数据时可能会出现问题。

一些性能分析测试表明,虽然几乎没有消耗CPU资源,但我的阵列复制代码花费了大量时间。争论很少,但很多时候都被阻止了。由于测试应用程序是单线程的,我假设有一些GC争用魔法正在进行。我对GC在这些情况下的工作方式不够自信,所以我在这里问。

我的问题 - 有没有办法创建新阵列,以减少GC的压力?或者是否有其他方法可以加快速度(为了测试和可读性目的而简化):

public MyCollection(MyCollection copyFrom)
{

    _items = new KeyValuePair<T, double>[copyFrom._items.Length]; //this line is reported to have a lot of contention time

    Array.Copy(copyFrom._items, _items, copyFrom._items.Length);

    _numItems = copyFrom._numItems;

}

3 个答案:

答案 0 :(得分:3)

不太确定这里发生了什么,但争用是线程问题,而不是数组复制问题。是的,并发分析器可能会指向 new 语句,因为内存分配需要获取保护堆的锁。

当分配来自gen#0堆时,该锁定用于非常短时间。因此,让线程争夺锁定而失去大量时间被锁定是一个非常不可能的事故。当分配来自大对象堆时,它不是那么快。当分配为85,000字节或更多时发生。但是,一个线程当然也会忙于复制数组元素。

请注意该工具告诉您的内容,非常多的争用自动意味着您遇到问题。当线程最终被阻塞大量时间时,它只会变得难看。如果 是一个真正的问题,那么接下来你需要查看垃圾收集花费了多少时间。有一个基本的perf计数器,你可以在Perfmon.exe中看到它。类别“.NET CLR Memory”,计数器“GC中的%时间”,instance = yourapp。考虑到你的复制量,这可能会很高。如果 是真正的问题,你可以调整一个旋钮是enable server GC

答案 1 :(得分:1)

persistent immutable data structure 的概念。这是可能的解决方案之一,基本上让你创建不可变对象,同时仍然以内存有效的方式修改它们。

例如,

Roslyn有一个不可变的SyntaxTree对象。您可以修改不可变对象,并获取修改后的不可变对象。请注意,“修改后的不可变对象”可能没有内存分配,因为它可以构建在“第一个不可变对象”上。

Visual Studio文本编辑器本身也使用相同的概念。 TextBuffer是不可变对象,但每次按下键盘按钮时,都会创建新的immutable TextBuffer,但是,它们不会分配内存(因为它会很慢)。

另外,如果您正面临LOH的问题,那么当您自己分配大内存块并将其用作“可重用”内存池时,它可能会有所帮助,从而完全避免GC。值得考虑。

答案 2 :(得分:0)

没有。您可以在2015年等待新的运行时,尽管这将使用SIMD指令进行Array.Copy操作。这将快得多。目前的实施非常不理想。

最后,诀窍在于避免内存操作 - 有时候这是不可能的。