Onde os literais de string Java e .NET residem?

Uma pergunta recente sobre literais de string no .NET chamou minha atenção. Eu sei que literais de string são internados para que diferentes strings com o mesmo valor se refiram ao mesmo object. Eu também sei que uma string pode ser internada em tempo de execução:

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

Obviamente, uma string que está internada em tempo de execução reside na pilha, mas eu tinha assumido que um literal é colocado no segmento de dados do programa (e disse isso na minha resposta à dita questão). No entanto, não me lembro de ver isso em qualquer lugar. Eu suponho que este é o caso, uma vez que é como eu faria isso e o fato de que a instrução ldstr IL é usada para obter literais e nenhuma alocação parece acontecer parece me apoiar.

Para encurtar a história, onde estão as literais de string? Está no heap, no segmento de dados ou em algum lugar em que não pensei?


Edit: Se literais de string residem no heap, quando eles são alocados?

   

    Strings no .NET são tipos de referência, portanto, eles estão sempre no heap (mesmo quando estão internados). Você pode verificar isso usando um depurador como o WinDbg.

    Se você tem a turma abaixo

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

    E você chama Foo() em uma instância, você pode usar o WinDbg para inspecionar o heap.

    A referência provavelmente será armazenada em um registrador para um programa pequeno, então o mais fácil é encontrar a referência para a string específica fazendo um !dso . Isso nos dá o endereço da nossa string em questão:

     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[]) 

    Agora use o !gcgen para descobrir em qual geração a instância está:

     0:000> !gcgen 025d2d04 Gen 0 

    Está na geração zero – isto é, acaba de ser alocado. Quem está torcendo?

     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) 

    O ESP é a pilha do nosso método Foo() , mas observe que também temos um object[] . Essa é a mesa interna. Vamos dar uma olhada.

     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 

    Eu reduzi a saída um pouco, mas você entendeu.

    Em conclusão : as strings estão no heap – mesmo quando estão internadas. A tabela internada contém uma referência à instância no heap. Ou seja, as strings internas não são coletadas durante o GC porque a tabela internada as organiza.

    Em Java (do Java Glossary ):

    Na JVM da Sun, as cadeias de caracteres internas (que incluem literais de cadeia) são armazenadas em um conjunto especial de RAM chamado perm gen, onde a JVM também carrega classs e armazena código compilado nativamente. No entanto, as cadeias de caracteres intered não se comportam de maneira diferente do que se tivessem sido armazenadas no heap de object comum.

    Corrija-me se estiver errado, mas nem todos os objects residem no heap, tanto em Java quanto em .NET?

    No .Net, os literais de string quando “internados” são armazenados em uma estrutura de dados especial denominada “tabela interna”. Isso é separado do heap e da pilha. No entanto, nem todas as strings são internadas … Tenho certeza de que aquelas que não estão são armazenadas na pilha.

    Não sabe sobre Java

    Eu encontrei isso no site do MSDN sobre a instrução ldstr IL :

    A instrução ldstr envia uma referência de object (tipo O) para um novo object de string representando o literal de string específico armazenado nos metadados. A instrução ldstr aloca a quantidade necessária de memory e realiza qualquer conversão de formato necessária para converter a string literal do formato usado no arquivo para o formato de string necessário no tempo de execução.

    A CLI (Common Language Infrastructure) garante que o resultado de duas instruções ldstr referentes a dois tokens de metadados que possuem a mesma sequência de caracteres retornam precisamente o mesmo object de string (um processo conhecido como “string interning”).

    Isso implica que os literais de string são de fato armazenados no heap no .NET (diferentemente do Java, como apontado por mmyers ).

    Em Java, strings como todos os objects residem no heap. Somente variables ​​primitivas locais (ints, chars e referências a objects) residem na pilha.

    As String’s Internas em java estão localizadas em um Pool separado chamado String Pool. Esse pool é mantido pela class String e reside no Heap normal (não no pool de Perm, como mencionado acima, que é usado para armazenar os dados da class).

    Pelo que entendi, nem todas as cadeias de caracteres são internadas, mas chamando myString.intern () retorna uma String que é garantida a partir do pool de String.

    Veja também: http://www.javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html e o javadoc http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String .html # intern ()