我正在使用C#中的垃圾收集器(或者更确切地说是CLR?)试图更好地理解C#中的内存管理。
我做了一个小样本程序,它将三个较大的文件读入byte[]
缓冲区。我想看看,如果
byte[]
设置为null时会产生任何影响GC.Collect()
免责声明:我使用Windows任务管理器测量内存消耗并将其四舍五入。我试了好几次,但总的来说还差不多。
这是我的简单示例程序:
static void Main(string[] args)
{
Loop();
}
private static void Loop()
{
var list = new List<string>
{
@"C:\Users\Public\Music\Sample Music\Amanda.wma", // Size: 4.75 MB
@"C:\Users\Public\Music\Sample Music\Despertar.wma", // Size: 5.92 MB
@"C:\Users\Public\Music\Sample Music\Distance.wma", // Size: 6.31 MB
};
Console.WriteLine("before loop");
Console.ReadLine();
foreach (string pathname in list)
{
// ... code here ...
Console.WriteLine("in loop");
Console.ReadLine();
}
Console.WriteLine(GC.CollectionCount(1));
Console.WriteLine("end loop");
Console.ReadLine();
}
对于每个测试,我只更改了foreach
循环的内容。然后我运行程序,在每个Console.ReadLine()
停止并检查Windows任务管理器中进程的内存使用情况。我记下了已用过的内存,然后继续程序返回(我知道断点;))。在循环结束后,我将GC.CollectionCount(1)
写入控制台,以便查看GC跳转的频率(如果有的话)。
<小时/> 测试1:
foreach ( ... )
{
byte[] buffer = File.ReadAllBytes(pathname);
Console.WriteLine ...
}
结果(使用的内存):
before loop: 9.000 K
1. iteration: 13.000 K
2. iteration: 19.000 K
3. iteration: 25.000 K
after loop: 25.000 K
GC.CollectionCount(1): 2
测试2:
foreach ( ... )
{
byte[] buffer = File.ReadAllBytes(pathname);
buffer = null;
Console.WriteLine ...
}
结果(使用的内存):
before loop: 9.000 K
1. iteration: 13.000 K
2. iteration: 14.000 K
3. iteration: 15.000 K
after loop: 15.000 K
GC.CollectionCount(1): 2
测试3:
foreach ( ... )
{
byte[] buffer = File.ReadAllBytes(pathname);
buffer = null;
GC.Collect();
Console.WriteLine ...
}
结果(使用的内存):
before loop: 9.000 K
1. iteration: 8.500 K
2. iteration: 8.600 K
3. iteration: 8.600 K
after loop: 8.600 K
GC.CollectionCount(1): 3
GC.CollectionCount
)。怎么会这样?buffer
设置为null
。内存低于测试2.但为什么GC.CollectionCount
输出2而不是3? 为什么内存使用量不如测试3中那么低? buffer
设置为null
)因此当通过GC.Collect()
调用垃圾收集器时它可以释放内存。看起来很清楚。如果有经验丰富的人能够对上述某些观点有所了解,那对我来说真的很有帮助。非常有趣的主题imho。
答案 0 :(得分:6)
考虑到您正在将整个WMA文件读入数组,我会说这些数组对象正在大对象堆中分配。这是一个单独的堆,它以更多malloc类型的方式进行管理(因为压缩垃圾收集在处理大型对象时效率不高)。
大对象堆中的空间是根据不同的规则收集的,它不计入主要的生成计数,这样你就不会看到测试1和2之间的集合数量有所不同虽然内存正在重用(所有收集的内容都是Array对象,而不是底层字节)。在测试3中,每次循环都强制收集一个集合 - 大对象堆被包含在其中,因此进程的内存使用量不会增加。
答案 1 :(得分:2)
TaskManager不是最好的工具。使用CLR Profiler或简单的事情,使用WriteLine显示GC.GetTotalMemory()
。
GC的主要目的是分配和取消分配大量小对象。如果你想学习它,写一些创造了很多(小)字符串的东西。确保你知道'代际GC'是什么意思。
您当前的实验正在行使大对象堆(LOH),其中包含一整套其他规则和关注点。
答案 2 :(得分:1)
答案 3 :(得分:0)
您通过任务管理器查看的内存使用情况是针对该过程的。请记住,CLR代表您的应用程序管理内存,因此您通常不会看到GC堆的使用直接反映在进程内存使用情况中。
分配和释放内存并不是免费的,所以很明显CLR会尝试对其进行优化以降低成本。因此,当从堆中收集对象时,您可能也可能看不到释放到操作系统的内存。