我知道什么是不可变性,以及String .NET类的特殊性。即使它是一个引用类型,不可变性使其表现得像一个值类型。明白了。C#参考文献强调了这一点(见string (C# Reference),我添加了强调):
以下是程序:
作为一个自学成才的程序员,我对垃圾收集器、内存泄漏和指针等方面并不精通。这就是为什么我正在问一个关于它的问题。C#编译器如何自动创建新的字符串对象并放弃旧的对象的描述让人感觉似乎会消耗大量的内存来处理废弃的字符串内容。许多对象都有销毁方法或析构函数,以便即使是自动化的CLR垃圾收集器也知道何时和如何清理不再需要的对象。但对于String类型却没有这样的内容。我想看看如果创建并立即放弃字符串对象,会发生什么情况。字符串是不可变的——在对象被创建后,字符串对象的内容不能更改,尽管语法使其似乎可以这样做。例如,当您编写此代码时,编译器实际上会创建一个新的字符串对象来保存新的字符序列,并将该新对象分配给b。然后,字符串"h"就有资格进行垃圾回收。
以下是程序:
class Program {
static void Main(string[] args)
{
Console.ReadKey();
int megaByte = (int)Math.Pow(1024, 2);
string[] hog = new string[2048];
char c;
for (int i = 0; i < 2048; i++)
{
c = Convert.ToChar(i);
Console.WriteLine("Generating iteration {0} (char = '{1}')", i, c);
hog[i] = new string(c, megaByte);
if ((i + 1) % 256 == 0) {
for (int j = (i - 255); j <= i; j++) { hog[j] = hog[i]; } }
}
Console.ReadKey();
List<string> uniqueStrings = new List<string>();
for (int i = 0; i < 2048; i++) {
if (!uniqueStrings.Contains(hog[i])) { uniqueStrings.Add(hog[i]); }
}
Console.WriteLine("There are {0} unique strings in hog.", uniqueStrings.Count);
Console.ReadKey();
// Create a timer with an interval of 30 minutes
// (30 minutes * 60 seconds * 1000 milliseconds)
System.Timers.Timer t = new System.Timers.Timer(30 * 60 * 1000);
t.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
t.Start();
Console.WriteLine("Waiting 30 minutes...");
Console.ReadKey();
}
static void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Console.WriteLine("Time's up. I'm collecting the garbage.");
GC.Collect();
}
}
它创建并销毁了大量唯一的字符串,但最终只留下8个唯一的字符串在hog数组中。在我的测试中,该进程仍然占用着570MB到1.1GB不等的内存。计时器部分等待30分钟,同时保持进程处于活动状态(不休眠),而在30分钟结束时,进程依然占用所有额外的内存,直到我强制进行垃圾回收。这使得 .NET 垃圾收集器似乎错过了某些东西。很多其他人说调用 GC.Collect() 是一件非常可怕的事情。因此,使用这种方法强制回收内存是唯一看起来可以恢复内存的方法,这仍然使问题看起来有些不对劲。
1024^2
是1026
而不是1048576
。 - Blindy