The*_*ler 5 .net c# mono garbage-collection memory-leaks
在测试应用程序性能时,我遇到了一些非常奇怪的GC行为.简而言之,GC甚至在没有运行时分配的空程序上运行!
以下应用程序演示了此问题:
using System;
using System.Collections.Generic;
public class Program
{
// Preallocate strings to avoid runtime allocations.
static readonly List<string> Integers = new List<string>();
static int StartingCollections0, StartingCollections1, StartingCollections2;
static Program()
{
for (int i = 0; i < 1000000; i++)
Integers.Add(i.ToString());
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
static void Main(string[] args)
{
DateTime start = DateTime.Now;
int i = 0;
Console.WriteLine("Test 1");
StartingCollections0 = GC.CollectionCount(0);
StartingCollections1 = GC.CollectionCount(1);
StartingCollections2 = GC.CollectionCount(2);
while (true)
{
if (++i >= Integers.Count)
{
Console.WriteLine();
break;
}
// 1st test - no collections!
{
if (i % 50000 == 0)
{
PrintCollections();
Console.Write(" - ");
Console.WriteLine(Integers[i]);
//System.Threading.Thread.Sleep(100);
// or a busy wait (run in debug mode)
for (int j = 0; j < 50000000; j++)
{ }
}
}
}
i = 0;
Console.WriteLine("Test 2");
StartingCollections0 = GC.CollectionCount(0);
StartingCollections1 = GC.CollectionCount(1);
StartingCollections2 = GC.CollectionCount(2);
while (true)
{
if (++i >= Integers.Count)
{
Console.WriteLine("Press any key to continue...");
Console.ReadKey(true);
return;
}
DateTime now = DateTime.Now;
TimeSpan span = now.Subtract(start);
double seconds = span.TotalSeconds;
// 2nd test - several collections
if (seconds >= 0.1)
{
PrintCollections();
Console.Write(" - ");
Console.WriteLine(Integers[i]);
start = now;
}
}
}
static void PrintCollections()
{
Console.Write(Integers[GC.CollectionCount(0) - StartingCollections0]);
Console.Write("|");
Console.Write(Integers[GC.CollectionCount(1) - StartingCollections1]);
Console.Write("|");
Console.Write(Integers[GC.CollectionCount(2) - StartingCollections2]);
}
}
Run Code Online (Sandbox Code Playgroud)
有人能解释一下这里发生了什么吗?我的印象是,除非内存压力达到特定限制,否则GC不会运行.然而,它似乎一直在运行(和收集) - 这是正常的吗?
编辑:我修改了程序以避免所有运行时分配.
编辑2:好的,新的迭代,似乎DateTime是罪魁祸首.其中一个DateTime方法分配内存(可能是Subtract),这会导致GC运行.第一个测试现在完全没有收集 - 正如预期的那样 - 而第二个导致几个收集.
简而言之,GC只在需要运行时运行 - 我只是在不知不觉中产生内存压力(DateTime是一个结构,我认为它不会产生垃圾).
GC.CollectionCount(0)
返回以下内容:
自进程启动以来指定生成的垃圾收集次数.
因此,您应该看到数量增加,并且这种增加并不意味着内存泄漏但GC已经运行.
同样在第一种情况下,您可以看到这种增加.它会发生得慢得多,因为非常慢的Console.WriteLine
方法被更频繁地调用,减慢了很多东西.
感谢大家!您的建议有助于揭示罪魁祸首:DateTime
正在分配堆内存。
GC 不会一直运行,而是仅在分配内存时运行。如果内存使用量持平,则将GC
永远不会运行,并且GC.CollectionCount(0)
将始终返回0
,如预期的那样。
测试的最新迭代展示了这种行为。第一个测试运行不分配任何堆内存(GC.CollectionCount(0)
剩余0
),而第二个测试运行以不明显的方式分配内存:通过DateTime.Subtract()
-> Timespan
。
现在, 和DateTime
都是Timespan
值类型,这就是为什么我发现这种行为令人惊讶。尽管如此,你还是明白了:毕竟存在内存泄漏。
归档时间: |
|
查看次数: |
1894 次 |
最近记录: |