Java 和 .NET 字符串文本位于何处?

2022-09-01 11:08:06

最近一个关于 .NET 中字符串文本的问题引起了我的注意。我知道字符串文本被插入,以便具有相同值的不同字符串引用同一对象。我也知道字符串可以在运行时进行暂存:

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

显然,在运行时滞留的字符串驻留在堆上,但我假设在程序的数据段中放置了一个文本(并在我对所述问题的回答中这样说)。但是我不记得在任何地方看到过这个。我假设情况就是这样,因为这是我这样做的方式,并且IL指令用于获取文本并且似乎没有进行任何分配的事实似乎支持我。ldstr

长话短说,字符串文本位于何处?它是在堆上,数据段上还是某个我没有想到的地方?


编辑:如果字符串文本确实驻留在堆上,何时分配它们?


答案 1

.NET 中的字符串是引用类型,因此它们始终位于堆上(即使它们被暂存)。您可以使用调试器(如 WinDbg)对此进行验证。

如果您有以下课程

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

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

引用很可能存储在小程序的寄存器中,因此最简单的方法是通过执行 .这为我们提供了所讨论的字符串的地址:!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

我稍微降低了输出,但你明白了。

总而言之:字符串在堆上 - 即使它们被拘禁。暂存表保存对堆上实例的引用。即,在 GC 期间不会收集滞留字符串,因为封存表会根植于它们。


答案 2

在Java中(来自Java术语表):

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


推荐