为什么默认情况下只有文本字符串保存在实习池中?

时间:2011-12-14 17:35:26

标签: c# .net string compiler-construction clr

为什么默认情况下只有文字字符串保存在实习池中?

来自MSDN的示例:

String s1 = "MyTest";
String s2 = new StringBuilder().Append("My").Append("Test").ToString(); 
String s3 = String.Intern(s2); 
Console.WriteLine("s1 == '{0}'", s1);
Console.WriteLine("s2 == '{0}'", s2);
Console.WriteLine("s3 == '{0}'", s3);
Console.WriteLine("Is s2 the same reference as s1?: {0}", (Object)s2==(Object)s1); 
Console.WriteLine("Is s3 the same reference as s1?: {0}", (Object)s3==(Object)s1);

/*
This example produces the following results:
s1 == 'MyTest'
s2 == 'MyTest'
s3 == 'MyTest'
Is s2 the same reference as s1?: False
Is s3 the same reference as s1?: True
*/

3 个答案:

答案 0 :(得分:57)

简短的回答:实习文字字符串在运行时便宜节省内存。实时非文字字符串在运行时非常昂贵,因此可以节省少量内存,以换取常见情况更慢

运行时实习字符串的成本"优化"不支付利益,因此实际上不是优化。实习文字字符串的成本很低,因此确实可以带来好处。

我在这里更详细地回答你的问题:

http://blogs.msdn.com/b/ericlippert/archive/2009/09/28/string-interning-and-string-empty.aspx

答案 1 :(得分:23)

语言设计者决定实际上每个中间字符串值的成本不值得性能成本。垃圾收集字符串的实习需要一个全局弱映射,当您拥有大量线程时,它可能成为瓶颈。

答案 2 :(得分:3)

在大多数字符串使用情况下,即使有一个零成本的弱引用实习池(理想的实习实现),实习字符串也几乎没有任何好处。为了使字符串实习能够提供任何好处,必须在相当长的时间内保持对重合等字符串的多次引用。

考虑以下两个程序:

  1. 从文本文件中输入100,000行,每行包含一些任意文本,然后输入100,000个五位数字。将读入的每个数字作为从零开始的索引读入到已读入的100,000行的列表中,并将相应的行输出到输出。
  2. 从文本文件输入100,000行,输出包含字符序列“fnord”的每一行。

对于第一个程序,根据文本文件的内容,字符串实习可能会在内存中产生几乎50,000:1的节省(如果该行包含100,000条相同的长行文本)或者可能代表完全浪费(如果全部100,000行不同)。在没有字符串实习的情况下,具有100,000个相同行的输入文件将导致同一字符串的100,000个实时实例同时存在 。使用字符串实习,实时实例的数量可以减少到两个。当然,编译器甚至无法猜测输入文件是否容易包含100,000条相同的行,100,000条不同的行或介于两者之间的东西。

对于第二个程序,即使理想的字符串实习实现也不太可能提供很多好处。即使输入文件的所有100,000行恰好相同,实习也无法节省大量内存。实习的效果不是阻止创建冗余字符串实例,而是允许识别和丢弃冗余字符串实例。由于每一行都可以在被检查和输出之后被丢弃,因此唯一可以购买的内容就是(理论上)丢弃冗余字符串实例的能力(非常)比其他方式稍早一点。

在某些情况下,缓存某些“中间”字符串结果可能会有好处,但这是一项最好留给程序员的任务。例如,我有一个程序需要将大量字节转换为两位十六进制字符串。为了方便起见,我有一个包含255个字符串的数组,它们保存从00到FF的字符串等价值。我知道,平均而言,该阵列中的每个字符串将至少使用数百或数千次,因此缓存这些字符串是一个巨大的胜利。另一方面,字符串只能被缓存,因为我知道它们代表什么。我可能知道,对于任何n 0-255,String.Format("{0:X2}",n)将始终产生相同的值,但我不希望编译器知道这一点。