.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 です。つまり、割り当てられたばかりです。誰がそれを応援していますか?
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 中に収集されません。これは、インターンされたテーブルがそれらをルート化するためです。