【问题标题】:Where do Java and .NET string literals reside?Java 和 .NET 字符串文字驻留在哪里?
【发布时间】:2010-09-27 05:13:18
【问题描述】:

.NET 中最近的 question about string literals 引起了我的注意。我知道字符串文字是interned,因此具有相同值的不同字符串引用同一个对象。我也知道可以在运行时实习字符串:

string now = DateTime.Now.ToString().Intern(); 

显然,一个在运行时被实习的字符串驻留在堆上,但我假设一个文字被放置在程序的数据段中(并在我的answer 中这样说)。但是我不记得在任何地方看到过这个。我认为是这种情况,因为我会这样做,而且 ldstr IL 指令用于获取文字并且似乎没有发生分配的事实似乎支持了我。

长话短说,字符串字面量在哪里?是在堆上、数据段上还是我没想到的地方?


编辑:如果字符串文字确实驻留在堆上,它们是什么时候分配的?

【问题讨论】:

    标签: java .net string-literals string-table


    【解决方案1】:

    .NET 中的字符串是引用类型,所以它们总是在堆上(即使它们被实习)。您可以使用诸如 WinDbg 之类的调试器来验证这一点。

    如果你有以下课程

       class SomeType {
          public void Foo() {
             string s = "hello world";
             Console.WriteLine(s);
             Console.WriteLine("press enter");
             Console.ReadLine();
          }
       }
    

    并且你在一个实例上调用Foo(),你可以使用WinDbg来检查堆。

    引用很可能存储在一个小程序的寄存器中,因此最简单的方法是通过!dso 找到对特定字符串的引用。这为我们提供了相关字符串的地址:

    0:000> !dso
    OS Thread Id: 0x1660 (0)
    ESP/REG  Object   Name
    002bf0a4 025d4bf8 Microsoft.Win32.SafeHandles.SafeFileHandle
    002bf0b4 025d4bf8 Microsoft.Win32.SafeHandles.SafeFileHandle
    002bf0e8 025d4e5c System.Byte[]
    002bf0ec 025d4c0c System.IO.__ConsoleStream
    002bf110 025d4c3c System.IO.StreamReader
    002bf114 025d4c3c System.IO.StreamReader
    002bf12c 025d5180 System.IO.TextReader+SyncTextReader
    002bf130 025d4c3c System.IO.StreamReader
    002bf140 025d5180 System.IO.TextReader+SyncTextReader
    002bf14c 025d5180 System.IO.TextReader+SyncTextReader
    002bf15c 025d2d04 System.String    hello world             // THIS IS THE ONE
    002bf224 025d2ccc System.Object[]    (System.String[])
    002bf3d0 025d2ccc System.Object[]    (System.String[])
    002bf3f8 025d2ccc System.Object[]    (System.String[])
    

    现在使用!gcgen 找出实例在哪一代:

    0:000> !gcgen 025d2d04 
    Gen 0
    

    它处于零代 - 即它刚刚被分配。谁在支持它?

    0:000> !gcroot 025d2d04 
    Note: Roots found on stacks may be false positives. Run "!help gcroot" for
    more info.
    Scan Thread 0 OSTHread 1660
    ESP:2bf15c:Root:025d2d04(System.String)
    Scan Thread 2 OSTHread 16b4
    DOMAIN(000E4840):HANDLE(Pinned):6513f4:Root:035d2020(System.Object[])->
    025d2d04(System.String)
    

    ESP 是我们的Foo() 方法的堆栈,但请注意我们也有一个object[]。那是实习生表。一起来看看吧。

    0:000> !dumparray 035d2020
    Name: System.Object[]
    MethodTable: 006984c4
    EEClass: 00698444
    Size: 528(0x210) bytes
    Array: Rank 1, Number of elements 128, Type CLASS
    Element Methodtable: 00696d3c
    [0] 025d1360
    [1] 025d137c
    [2] 025d139c
    [3] 025d13b0
    [4] 025d13d0
    [5] 025d1400
    [6] 025d1424
    ...
    [36] 025d2d04  // THIS IS OUR STRING
    ...
    [126] null
    [127] null
    

    我稍微减少了输出,但你明白了。

    结论:字符串在堆上——即使它们被实习。实习表保存对堆上实例的引用。 IE。在 GC 期间不会收集实习字符串,因为实习表将它们作为根。

    【讨论】:

      【解决方案2】:

      在 Java 中(来自Java Glossary):

      在 Sun 的 JVM 中,内部字符串(包括字符串文字)存储在称为 perm gen 的特殊 RAM 池中,JVM 还加载类并存储本地编译的代码。但是,intered String 的行为与它们存储在普通对象堆中的行为没有什么不同。

      【讨论】:

      • 应该找到一个规范的参考。您不能随便引用或引用任意互联网垃圾。
      【解决方案3】:

      如果我错了,请纠正我,但在 Java 和 .NET 中并非所有对象都驻留在堆上?

      【讨论】:

      • .NET 中的值类型驻留在堆栈上,除非它们是引用类型的一部分,在这种情况下它们位于堆上。
      • 对,我会从“对象”类别中排除值类型,但我又习惯了 Java 而不是 .NET
      • 如果值类型足够小,它们甚至可能不在堆栈中,而只在寄存器中。
      【解决方案4】:

      在 .Net 中,“实习”时的字符串文字存储在称为“实习表”的特殊数据结构中。这与堆和堆栈是分开的。然而,并不是所有的字符串都被保留了......我很确定那些不是存储在堆上的。

      不懂Java

      【讨论】:

      • 肯定实习生表只保存对字符串的引用,而不存储构成字符串的实际字节?
      • 实习表保存对堆上字符串的引用。
      【解决方案5】:

      我在 MSDN 的网站上发现了这个关于 ldstr IL instruction:

      ldstr 指令将对象引用(O 类型)推送到表示存储在元数据中的特定字符串文字的新字符串对象。 ldstr 指令分配必要的内存量并执行将字符串文字从文件中使用的形式转换为运行时所需的字符串格式所需的任何格式转换。

      公共语言基础结构 (CLI) 保证引用两个具有相同字符序列的元数据标记的两个 ldstr 指令的结果返回完全相同的字符串对象(称为“字符串实习”的过程)。

      这意味着字符串文字实际上存储在 .NET 中的堆上(与 Java 不同,pointed out mmyers)。

      【讨论】:

      • 不,它只是说它们的行为就像它们存储在普通堆上一样
      【解决方案6】:

      在 Java 中,字符串和所有对象一样都驻留在堆中。 只有局部原始变量(整数、字符和对对象的引用)驻留在堆栈中。

      【讨论】:

        【解决方案7】:

        Java 中的内部字符串位于一个单独的池中,称为字符串池。该池由 String 类维护,驻留在普通 Heap 上(不是上面提到的用于存储类数据的 Perm 池)。

        据我了解,并非所有字符串都被实习,但调用 myString.intern() 会返回字符串池保证的字符串。

        另请参阅: http://www.javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html 和javadoc http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html#intern()

        【讨论】:

        • 在文字字符串的情况下由编译器和类加载器维护。曾经它确实在 PermGen 中。
        猜你喜欢
        • 2023-03-29
        • 2015-04-26
        • 1970-01-01
        • 2011-07-01
        • 2013-07-23
        • 2012-07-12
        • 2015-10-24
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多