编译器会优化字符串字面量吗?

21

C#编译器或.NET CLR是否对字符串文字/常量进行了任何智能的内存优化?我记得听说过“字符串内部化”的概念,即在程序中的任意两个代码部分中,文字“this is a string”实际上将引用相同的对象(假定是安全的,因为字符串是不可变的)。然而,在谷歌上找不到有用的参考资料...

我是否听错了?别担心-我在我的代码中没有利用这些信息做什么可怕的事情,只是想更好地了解它的工作原理。


相关 https://dev59.com/MXRC5IYBdhLWcg3wOOT1 - Brian Rasmussen
3个回答

21

编辑:虽然我强烈怀疑下面的说法适用于所有的C#编译器实现,但我不确定它在规范中是否有保证。规范第2.4.4.5节谈到字面值(literals)引用相同的字符串实例,但没有提到其他常量字符串表达式。我怀疑这是规范中的疏漏 - 我将给Mads和Eric发送电子邮件。


它不仅限于字符串字面量,还包括任何字符串常量。例如,考虑:

public const string X = "X";
public const string Y = "Y";
public const string XY = "XY";

void Foo()
{
    string z = X + Y;
}
编译器意识到这里的连接操作(对于 z)是两个常量字符串之间的,因此结果也是一个常量字符串。因此,z 的初始值将与 XY 的值相同引用,因为它们都是编译时常量,并且具有相同的值。
编辑:Mads 和 Eric 的回复表明,在 Microsoft C# 编译器中,字符串常量和字符串字面量通常被视为相同的方式处理-但其他实现可能不同。

两个不同程序集中的相同字符串常量指向同一个对象吗?/ JIT 是否会对字符串字面量进行内部化处理? - CodesInChaos
@CodeInChaos: 我认为这取决于 CompilationRelaxationsAttribute(CompilationRelaxations.NoStringInterning) 属性。不过我也不能确定。 - Jon Skeet
嗨@JonSkeet,请问同样内容的interned字符串是否总是具有相同的引用?这是否意味着比较这些字符串的引用将返回true? - Johnny_D
1
@Johnny_D:是的,而且是肯定的 - 至少在同一程序集中保证。在不同程序集之间就会变得更加棘手,如果我没记错的话。 - Jon Skeet

10

这篇文章很好地解释了字符串驻留。引用:

.NET 拥有一个“驻留池”的概念。它基本上只是一组字符串,但它确保每次引用相同的字符串字面量时,您都会得到对同一字符串的引用。这可能因语言而异,但在 C# 和 VB.NET 中肯定是正确的,如果有语言不适用此规则,我将非常惊讶,因为IL使其非常容易实现(可能比不驻留字面量更容易)。除了字面量被自动驻留之外,您还可以使用Intern方法手动驻留字符串,并使用IsInterned方法检查池中是否已存在具有相同字符序列的驻留字符串。这有些令人费解地返回一个字符串而不是布尔值-如果池中存在等于的字符串,则返回对该字符串的引用。否则,返回null。同样,Intern方法返回对驻留字符串的引用-如果传递的字符串已经在池中,则返回该字符串,否则返回新创建的驻留字符串或已经在池中的等效字符串。


1
附注:由于应用程序域的生存期内不会释放 interned 字符串,不正确使用 intering 可能会导致内存泄漏。 - CodesInChaos

9

是的,它可以优化字符串字面量。一个简单的示例,您可以看到:

string s1="A";
string s2="A";
object.ReferenceEquals(s1,s2);  //true

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接