当程序创建大量对象时,C#应用程序CPU性能急剧下降
我遇到了一个奇怪的性能问题:当程序创建大量对象时,C#应用程序CPU性能急剧下降,c#,performance,C#,Performance,我遇到了一个奇怪的性能问题: 我有一个C#应用程序,它可以创建数百万个C#对象 在代码的一个不相关部分中,应用程序执行特定的工作,该工作不依赖于在步骤1中分配的数据 CPU时间似乎与步骤1中创建的对象数量相关 我写了一个简单的案例,再现了我的问题。 在调用DoMyWork()方法之前,使用创建的数以百万计的字符串对象数调用slood命令。 如您所见,如果实例化了2亿个字符串,则相同的DoMyWork()方法可能需要3秒钟的时间 我是否遗漏了语言中的某些内容? 假设未达到物理内存限制,是否存在不
DoMyWork()
方法之前,使用创建的数以百万计的字符串对象数调用slood
命令。
如您所见,如果实例化了2亿个字符串,则相同的DoMyWork()
方法可能需要3秒钟的时间
- 我是否遗漏了语言中的某些内容?
- 假设未达到物理内存限制,是否存在不应达到的最大对象数,否则CLR将减速?
使用系统;
使用系统诊断;
使用系统集合;
名称空间减速
{
班级计划
{
静态字符串[]arr;
静态空心CreateHugeStringArray(长尺寸)
{
arr=新字符串[大小*1000000];
对于(int i=0;i
很可能是垃圾收集器的讨厌东西,它会冻结主线程,即使它主要在后台线程上工作,如下所述:
如果您收集它,无论“不相关”数组的大小如何,时间都保持在90毫秒左右(在我的例子中)。该问题是由于垃圾收集器与您的
DoMyWork同时运行造成的。它需要清理的阵列的绝对大小会“中断”实际工作
要查看GC的影响,请在StartNew
调用之前添加以下行-以便GC工作在计时之前发生:
GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce;
GC.Collect();
以下代码创建10000个新字符串对象,强制运行垃圾收集:
string str = "";
for (int i = 0; i < 10000; i++) str += i;
string str=”“;
对于(inti=0;i<10000;i++)str++=i;
垃圾收集器的性能与
- 已分配的对象数
- 正在使用的内存总量
CreateHugeStringArray()分配非常大的对象,从而增加了使用的内存总量。在极端情况下,部分内存可能在磁盘上(被调出),从而进一步降低系统速度
你的故事的寓意是——除非你需要内存,否则不要分配内存。还没有找到原因,但似乎LOH中有一个巨大的数组会显著降低垃圾收集的速度。
但是,如果我们创建许多较小的数组来保存相同数量的数据(这将进入第2代而不是LOH),GC不会慢很多。似乎带有1kk字符串指针的数组占用了大约400万字节的内存。因此,为了避免到达LOH,数组必须占用少于85 KB的空间。这大约要少50倍。您可以使用旧技巧将大数组拆分为多个小数组
private static string[][] arrayTwoDimentional;
private static int _arrayLength = 1000000;
private static int _sizeFromExample = 200;
static void CreateHugeStringArrayTwoDimentional()
{
// Make 50 times more smaller arrays
arrayTwoDimentional = new string[_sizeFromExample * 50][];
for (long i = 0; i < arrayTwoDimentional.Length; i++)
{
// Make array smaller 50 times
arrayTwoDimentional[i] = new string[_arrayLength / 50];
for (var index = 0; index < arrayTwoDimentional[i].Length; index++)
{
arrayTwoDimentional[i][index] = "";
}
}
}
static string GetByIndex(long index)
{
var arrayLenght = _arrayLength / 50;
var firstIndex = index / arrayLenght;
var secondIndex = index % arrayLenght;
return arrayTwoDimentional[firstIndex][secondIndex];
}
私有静态字符串[][]数组二维;
私有静态int_arraylelength=1000000;
私有静态int_sizeFromExample=200;
静态void CreateHugeStringArray二维()
{
//使阵列更小50倍
ArrayTwoDimensional=新字符串[_SizeFromeExample*50]];
for(长i=0;i
证明GC是这里的瓶颈
替换阵列布局后
在本例中,数组大小是硬编码的。Codeproject中有一个很好的例子,说明如何计算类型存储对象的大小,这将有助于调整数组的大小:
在您的示例中,您不会创建“数百万个C#对象”。您创建了一个庞大的字符串数组,每个数组元素实际上都指向同一个字符串对象(请参见string)
string str = "";
for (int i = 0; i < 10000; i++) str += i;
private static string[][] arrayTwoDimentional;
private static int _arrayLength = 1000000;
private static int _sizeFromExample = 200;
static void CreateHugeStringArrayTwoDimentional()
{
// Make 50 times more smaller arrays
arrayTwoDimentional = new string[_sizeFromExample * 50][];
for (long i = 0; i < arrayTwoDimentional.Length; i++)
{
// Make array smaller 50 times
arrayTwoDimentional[i] = new string[_arrayLength / 50];
for (var index = 0; index < arrayTwoDimentional[i].Length; index++)
{
arrayTwoDimentional[i][index] = "";
}
}
}
static string GetByIndex(long index)
{
var arrayLenght = _arrayLength / 50;
var firstIndex = index / arrayLenght;
var secondIndex = index % arrayLenght;
return arrayTwoDimentional[firstIndex][secondIndex];
}