为什么.NET IL总是创建新的字符串对象,即使更高级别的代码引用现有的?

Kev*_* D. 4 .net

背景: 我们有一个包含数千个伪代码函数的XML文档.我编写了一个实用程序来解析这个文档并从中生成C#代码.这是生成的代码的大大简化的片段:

public class SomeClass
{
    public string Func1() { return "Some Value"; }
    public string Func2() { return "Some Other Value"; }
    public string Func3() { return "Some Value"; }
    public string Func4() { return "Some Other Value"; }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

重要的一点是每个字符串值可能会被多种方法返回.我假设通过进行一个小的更改,以便方法将返回对静态成员字符串的引用,这将减少程序集大小并减少程序的内存占用.例如:

public class SomeClass
{
    private const string _SOME_VALUE = "Some Value";
    private const string _SOME_OTHER_VALUE = "Some Other Value";
    // ...

    public string Func1() { return _SOME_VALUE; }
    public string Func2() { return _SOME_OTHER_VALUE; }
    public string Func3() { return _SOME_VALUE; }
    public string Func4() { return _SOME_OTHER_VALUE; }
    // ...
}
Run Code Online (Sandbox Code Playgroud)

但令我惊讶的是,使用.NET ildasm.exe实用程序进行检查表明,在这两种情况下,函数的IL都是相同的.这是其中之一.无论哪种方式,硬编码值都与ldstr一起使用:

.method public hidebysig instance string
        Func1() cil managed
{
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  ldstr      "Some Value"
  IL_0005:  ret
} // end of method SomeClass::Func1
Run Code Online (Sandbox Code Playgroud)

实际上,"优化"版本稍微差一些,因为它包含程序集中的静态字符串成员.当我使用除字符串之外的其他对象类型重复此实验时,我看到了我期望的差异.请注意,在启用优化的情况下生成程序集.

问题: 为什么.NET显然总是创建一个新的字符串对象,无论代码是否引用现有的?

Han*_*ant 6

  IL_0000:  ldstr      "Some Value"
  IL_0005:  ret
Run Code Online (Sandbox Code Playgroud)

反汇编程序太有帮助了,无法向您展示实际情况.您可以从IL地址判断,请注意ldstr指令只需要5个字节.太少存储该字符串的方式.使用View + Show标记值来查看它的真实外观.您现在还将看到相同的字符串使用相同的标记值.这被称为'实习'.

令牌值仍然没有显示程序被jitted后字符串真正存储在何处.字符串文字进入'loader heap',这是一个与垃圾收集堆不同的堆.它是存储静态项的堆.或者换句话说:字符串文字是高度优化的并且非常便宜.你自己做得不好.