4

我创建了一个示例 C# 控制台应用程序,它读取字节数组中的文件数据并将字节数组转换为十六进制字符串。它需要巨大的内存,并且在工作完成后它不会释放内存,我也在使正在使用的变量无效。

这是示例代码:

string filename = @"F:\\AVSEQ09.DAT"; //file size is 32 MB
string hexData = null;

byte[] fileDataContent = File.ReadAllBytes(filename);
if (fileDataContent != null)
    hexData = BitConverter.ToString(fileDataContent);
fileDataContent = null;
hexData = null;

//GC.Collect();
Console.ReadKey();

如果我运行此代码,则需要 433 MB 的私有工作集,如果我取消注释 GC.collect 调用,则内存将降至 6 MB。为什么我必须显式调用 GC.collect,显式调用 GC.collect 是否不好,如何在不调用 GC.collect 的情况下释放内存(至 6 MB)?

4

6 回答 6

9

垃圾收集是在内存有限的机器上模拟无限内存,通过回收有效程序无法注意到的内存丢失。

以上是一个非常重要的概念,因为除其他外,它强调了垃圾收集器不必任何事情,只要它可以在您的程序每次请求时提供内存即可。

它可能不是最可控的内存管理系统,但如果您的程序最终处于内存压力的情况下,CLR 通常会自动启动垃圾回收周期以减轻部分压力。当系统似乎没有压力时,收集被推迟,以避免频繁的不必要的暂停。

于 2013-04-22T08:10:58.453 回答
4

您不必GC.Collect显式调用。

您的代码确实使用了大量内存,但是垃圾收集有一个智能算法来确定它应该何时运行。它在您内存不足或内存使用量突然增加时运行。只要还有额外的内存,垃圾收集器就不会做任何事情。这大大提高了性能。如果 GC 持续运行,您的性能将很糟糕。

null在 .NET 中也不需要将变量设置为。运行时将跟踪正在使用的变量,并在必要时将它们标记为收集。

于 2013-04-22T08:11:18.987 回答
2

您不应GC明确使用,而应避免一次读取所有数据。这是您正在执行的相当糟糕的做法...从文件中读取时,您应该使用using-statement 并在小缓冲区中读取。

using(StreamReader sr = new StreamReader(filename)){
     //while has data
     char[] buffer = new char[64000]; // or whatever you like
     sr.ReadBlock(buffer, 0, buffer.Length);     
     //do your conversion here
}

这里寻找StreamReader.ReadBlock()

于 2013-04-22T08:10:39.310 回答
1

.NET CLR 自己管理内存,并在必要时调用垃圾收集器。

无论如何,即使您有时间要求严格的代码,您也可以依赖 .NET CLR 在适当的时间调用垃圾收集器。GC 非常高效,并且可以非常快地释放内存。

所以真的没有问题。不要调用GC,你不需要。这是.NET。

于 2013-04-22T08:09:36.213 回答
1

.NET 垃圾收集器是分代收集器。从代的角度来看,大对象(85K 或更大)属于第 2 代,因为它们仅在存在第 2 代收集时才被收集,包括所有代,这不像第 0 代收集那样经常发生,因为对象大小,大对象通常是数组(在您的情况下是字节 [])。例如,第 2 代收集可能由以下原因引起:

  • 分配超过第 0 代或大对象阈值 大多数 GC 发生是因为托管堆上的分配
  • 系统处于低内存情况 当我收到来自操作系统的高内存通知时会发生这种情况。
  • 当有人在第 2 代调用 GC.Collect 时调用 System.GC.Collect(通过不向 GC.Collect 传递任何参数或将 GC.MaxGeneration 作为参数传递)

因此,当您的系统需要内存时,它通常会释放字节数组,而无需您通过调用 GC.Collect 来强制它释放它。

看看这篇文章

于 2013-04-22T08:46:06.033 回答
1

当您处理如此大的文件和内存结构时,请考虑使用流而不是打开和转换内存中的完整文件。无论如何,您不应该调用 GC collect。

于 2013-04-22T08:10:35.110 回答