我理解不变性是什么,以及String .NET类是如何特殊的。不变性使得它的行为类似于值类型,即使它是引用类型。得到它了。 C#参考强调了这一点(见string (C# Reference),我强调的重点:
字符串是不可变的 - 创建对象后无法更改字符串对象的内容,尽管语法使其看起来好像可以执行此操作。例如,当您编写此代码时,编译器实际上会创建一个新的字符串对象来保存新的字符序列,并将该新对象分配给b。然后字符串“h”有资格进行垃圾回收。
作为一名自学成才的程序员,我不熟悉垃圾收集器和内存泄漏以及指针和内容。这就是我问这个问题的原因。 C#编译器如何自动创建新的字符串对象并放弃旧的字符串对象的描述使得看起来像一堆内存可能会被废弃的字符串内容用尽。许多对象都有处置方法或析构函数,因此即使是自动CLR垃圾收集器也知道何时以及如何在不再需要的对象之后进行清理。字符串没有这样的东西。我想看看如果创建一个程序会让我自己和其他人创建并立即放弃字符串对象会占用大量内存,实际会发生什么。
以下是该计划:
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数组中。在我的测试中,该过程仍然保持在570 Mb到1.1 Gb(它变化)。计时器部分等待30分钟,同时使进程保持活动状态(不休眠),在30分钟结束时,进程仍然保持所有额外的内存,直到我强行收集。这使得.NET垃圾收集器似乎错过了一些东西。很多其他地方的人都说调用GC.Collect()是可怕的。因此,内存似乎只是通过强制使用此方法的收集器来回收的事实仍然使得它看起来像是错误的。
答案 0 :(得分:8)
你的帖子有点膨胀。简而言之,您在保留引用的同时分配了大量内存,然后注意即使在不再引用GC时GC也不会触发,即使经过很长时间也是如此。
这意味着GC不是基于时间触发的,而是基于发生的分配。一旦没有分配,它就不会运行。如果再次开始分配,内存最终会消失。
这与不变性或特别是字符串无关。
答案 1 :(得分:4)
不变性意味着
string a = "abc";
string b = a;
a=a+"def";
创建一个包含“abcdef”的新字符串,并将其分配给a
。 b
的值保持不变,仍然引用包含“abc”的字符串。
答案 2 :(得分:2)
只是您的CLR版本的垃圾收集器对内存压力做出反应,而不是经过时间。为什么花费宝贵的CPU时间来清理不需要的内存呢?
当然,你可以说当程序“空闲”时执行GC会更好。问题是,您希望保持GC算法尽可能简单(这通常意味着快速)并且算法无法读懂您的想法。它不知道应用程序什么时候“没有任何建设性”。