最近在.NET的一个question about string literals引起了我的注意。我知道字符串文字是interned的,所以具有相同值的不同字符串引用相同的对象。我还知道一个字符串可以在运行时驻留:
string now = DateTime.Now.ToString().Intern();
显然,在运行时驻留的字符串驻留在堆中,但我假设在程序的数据段中放置了一个文字(并在我的answer中对上述问题进行了说明)。然而,我不记得在任何地方见过这个。我假设是这样的,因为我会这样做,而且ldstr
IL指令被用来获取字面值,似乎没有发生分配,这似乎支持我的观点。
长话短说,字符串文字驻留在哪里?它是在堆上、数据段上还是我没有想到的某个地方?
编辑:如果字符串字面值确实驻留在堆中,什么时候分配它们?
发布于 2008-12-16 20:22:06
.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
我在一定程度上减少了输出,但您已经明白了。
结论:字符串在堆上-即使它们被驻留。interned表包含对堆上实例的引用。也就是说,在GC过程中不会收集被占用的字符串,因为被占用的表根于它们。
发布于 2008-12-16 20:38:20
在Java中(从Java Glossary):
在Sun中,内部字符串(包括字符串文字)存储在一个称为perm gen的特殊内存池中,JVM还在其中装入类并存储本地编译的代码。但是,感兴趣的字符串的行为与它们存储在普通对象堆中的行为没有什么不同。
发布于 2008-12-16 20:21:28
如果我错了,请纠正我,但在Java和.NET中,不是所有的对象都驻留在堆上吗?
https://stackoverflow.com/questions/372547
复制相似问题