¿Dónde residen los literales de cadena Java y .NET?

Una pregunta reciente sobre literales de cadenas en .NET me llamó la atención. Sé que los literales de cadena están internados de modo que diferentes cadenas con el mismo valor se refieren al mismo objeto. También sé que una cadena puede ser internada en tiempo de ejecución:

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

Obviamente, una cadena que está internada en tiempo de ejecución reside en el montón, pero asumí que se coloca un literal en el segmento de datos del progtwig (y así lo dije en mi respuesta a dicha pregunta). Sin embargo, no recuerdo haber visto esto en ninguna parte. Supongo que este es el caso, ya que es la forma en que lo haría y el hecho de que la instrucción ldstr IL se utiliza para obtener los literales y parece que no se realiza ninguna asignación parece respaldarme.

Para abreviar, ¿dónde residen los literales de cadenas? ¿Está en el montón, en el segmento de datos o en algún lugar en el que no he pensado?


Editar: Si los literales de cadena residen en el montón, ¿cuándo se asignan?

Las cadenas en .NET son tipos de referencia, por lo que siempre están en el montón (incluso cuando están internados). Puede verificar esto usando un depurador como WinDbg.

Si tienes la clase a continuación

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

Y llama a Foo() en una instancia, puede usar WinDbg para inspeccionar el montón.

La referencia probablemente se almacenará en un registro para un progtwig pequeño, por lo que lo más fácil es encontrar la referencia a la cadena específica haciendo un !dso . Esto nos da la dirección de nuestra cadena en cuestión:

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

Ahora usa !gcgen para descubrir en qué generación está la instancia:

 0:000> !gcgen 025d2d04 Gen 0 

Está en la generación cero, es decir, acaba de asignarse. ¿Quién lo está rooteando?

 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) 

El ESP es la stack para nuestro método Foo() , pero note que también tenemos un object[] . Esa es la mesa de pasante. Vamos a ver.

 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 

Reduje un poco la producción, pero entiendes la idea.

En conclusión : las cadenas están en el montón, incluso cuando están internados. La tabla interna contiene una referencia a la instancia en el montón. Es decir, las cadenas internas no se recostackn durante el GC porque la tabla interna las enraiza.

En Java (del Glosario de Java ):

En la JVM de Sun, las cadenas internas (que incluyen literales de cadena) se almacenan en un grupo especial de RAM llamado generador de perm, donde la JVM también carga clases y almacena el código comstackdo de forma nativa. Sin embargo, las cadenas interpretadas no se comportan de forma diferente a como se habían almacenado en el montón de objetos ordinarios.

Corrígeme si estoy equivocado, pero ¿no residen todos los objetos en el montón, tanto en Java como en .NET?

En .Net, los literales de cadena cuando están “internados”, se almacenan en una estructura de datos especial llamada, la “tabla interna”. Esto está separado del montón y la stack. No obstante, no todos los hilos están internados … Estoy bastante seguro de que los que no están almacenados en el montón.

No sé sobre Java

Encontré esto en el sitio de MSDN sobre la instrucción ldstr IL :

La instrucción ldstr empuja una referencia de objeto (tipo O) a un nuevo objeto de cadena que representa el literal de cadena específico almacenado en los metadatos. La instrucción ldstr asigna la cantidad de memoria requerida y realiza cualquier conversión de formato necesaria para convertir el literal de cadena del formulario utilizado en el archivo al formato de cadena requerido en el tiempo de ejecución.

Common Language Infrastructure (CLI) garantiza que el resultado de dos instrucciones ldstr que hacen referencia a dos tokens de metadatos que tienen la misma secuencia de caracteres devuelve precisamente el mismo objeto de cadena (un proceso conocido como “cadena interna”).

Esto implica que los literales de cadena de hecho están almacenados en el montón en .NET (a diferencia de Java según lo señalado por mmyers ).

En Java, las cadenas como todos los objetos residen en el montón. Solo las variables primitivas locales (ints, caracteres y referencias a objetos) residen en la stack.

Las cadenas internas en Java están ubicadas en un grupo separado llamado String Pool. Este grupo es mantenido por la clase String y reside en el Heap normal (no en el grupo Perm como se mencionó anteriormente, que se usa para almacenar los datos de clase).

Como entiendo, no todas las cadenas están intercaladas, pero al llamar myString.intern () se devuelve una cadena que está garantizada desde el grupo de cadenas.

Ver también: http://www.javaranch.com/journal/200409/ScjpTipLine-StringsLiterally.html y el javadoc http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String .html # pasante ()