.NET中最近的question about string literals引起了我的注意。我知道字符串文字是interned,因此具有相同值的不同字符串引用同一个对象。我也知道字符串可以在运行时实现:
string now = DateTime.Now.ToString().Intern();
显然,在运行时实例化的字符串驻留在堆上,但我假设文字放在程序的数据段中(并在我的answer中对所述问题这样说)。但是我不记得在任何地方看到这个。我认为情况就是这样,因为我是这样做的,并且ldstr
IL指令用于获取文字并且似乎没有进行分配的事实似乎支持我。
简而言之,字符串文字在哪里?它是在堆上,数据段还是我没想过的某个地方?
编辑:如果字符串文字执行驻留在堆上,它们何时分配?
答案 0 :(得分:107)
.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期间不会收集实习字符串,因为实习表会根据它们进行收集。
答案 1 :(得分:12)
在Java中(来自Java Glossary):
在Sun的JVM中,实习的字符串(包括字符串文字)存储在称为perm gen的特殊RAM池中,其中JVM还加载类并存储本机编译的代码。但是,intered Strings的行为与它们存储在普通对象堆中的行为没有区别。
答案 2 :(得分:3)
如果我错了,请纠正我,但是在Java和.NET中,并不是所有对象都驻留在堆上?
答案 3 :(得分:1)
在.Net中,“interned”时的字符串文字存储在一个名为“intern table”的特殊数据结构中。这与堆和堆栈分开。然而,并非所有字符串都被实现...我很确定那些不存在的字符串存储在堆上。
不了解Java
答案 4 :(得分:1)
我在MSDN网站上发现了ldstr
IL instruction:
ldstr
指令将对象引用(类型O)推送到表示存储在元数据中的特定字符串文字的新字符串对象。ldstr
指令分配必要的内存量,并执行将字符串文字从文件中使用的表单转换为运行时所需的字符串格式所需的任何格式转换。公共语言基础结构(CLI)保证引用具有相同字符序列的两个元数据标记的两个ldstr指令的结果精确地返回相同的字符串对象(称为“字符串实习”的过程)。
这意味着字符串文字实际上存储在.NET中的堆上(与pointed out除mmyers之外的Java不同。
答案 5 :(得分:0)
在Java中,所有对象之类的字符串都驻留在堆中。 只有本地原始变量(整数,字符和对象的引用)驻留在堆栈中。
答案 6 :(得分:-1)
java中的Interned String位于一个名为String Pool的独立池中。该池由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()