2010-03-24 11 views
10

Estoy tratando de entender el interrogatorio de cadenas y por qué no parece funcionar en mi ejemplo. El objetivo del ejemplo es mostrar que el Ejemplo 1 usa menos (mucha menos memoria) ya que solo debería tener 10 cadenas en la memoria. Sin embargo, en el siguiente código ambos ejemplos usan aproximadamente la misma cantidad de memoria (tamaño virtual y conjunto de trabajo).C# string interning

Por favor, ¿por qué el ejemplo 1 no usa mucha menos memoria? Gracias

Ejemplo 1:

 IList<string> list = new List<string>(10000); 

     for (int i = 0; i < 10000; i++) 
     { 
      for (int k = 0; k < 10; k++) 
      { 
       list.Add(string.Intern(k.ToString())); 
      } 

     } 

     Console.WriteLine("intern Done"); 
     Console.ReadLine(); 

Ejemplo 2:

 IList<string> list = new List<string>(10000); 

     for (int i = 0; i < 10000; i++) 
     { 
      for (int k = 0; k < 10; k++) 
      { 
       list.Add(k.ToString()); 
      } 

     } 

     Console.WriteLine("intern Done"); 
     Console.ReadLine(); 
+1

¿No es esta pregunta mucho como la que usted pidió ayer? http://stackoverflow.com/questions/2502522/string-interning-should-this-code-only-create-10-strings-in-memory –

+0

sí, pero no es lo mismo – CodingThunder

Respuesta

2

Desde el msdnEn segundo lugar, al pasante una cadena, primero debe crear la cadena. La memoria utilizada por el objeto String aún debe asignarse, incluso aunque la memoria eventualmente sea basura.

16

El problema es que ToString() todavía asignará una nueva cadena, y luego pasante ella. Si el recolector de basura no se ejecuta para recopilar esas cadenas "temporales", el uso de la memoria será el mismo.

Además, la longitud de sus cadenas es bastante corta. 10,000 cadenas que en su mayoría tienen un solo carácter es una diferencia de memoria de aproximadamente 20 KB, que probablemente no notarás. Intente utilizar cadenas más largas (o un mucho más de ellos) y hacer una recolección de basura antes de comprobar el uso de la memoria.

Aquí es un ejemplo que hace muestran una diferencia:

class Program 
{ 
    static void Main(string[] args) 
    { 
     int n = 100000; 

     if (args[0] == "1") 
      WithIntern(n); 
     else 
      WithoutIntern(n); 
    } 

    static void WithIntern(int n) 
    { 
     var list = new List<string>(n); 

     for (int i = 0; i < n; i++) 
     { 
      for (int k = 0; k < 10; k++) 
      { 
       list.Add(string.Intern(new string('x', k * 1000))); 
      } 
     } 

     GC.Collect(); 
     Console.WriteLine("Done."); 
     Console.ReadLine(); 
    } 

    static void WithoutIntern(int n) 
    { 
     var list = new List<string>(n); 

     for (int i = 0; i < n; i++) 
     { 
      for (int k = 0; k < 10; k++) 
      { 
       list.Add(new string('x', k * 1000)); 
      } 
     } 

     GC.Collect(); 
     Console.WriteLine("Done."); 
     Console.ReadLine(); 
    } 
} 
+2

Micro-optimización típica que simplemente no funciona mostrar lo que se supone que debe hacer. – TomTom

+0

Mismo resultado con GC. Recolectar y usar cadenas más grandes .... hrmmmm – CodingThunder

+0

@ TomTom: ¿Qué? – CodingThunder

7

Recuerde, el CLR administra la memoria en nombre de su proceso, por lo que es realmente difícil determinar el tamaño de la memoria administrada al observar el tamaño virtual y el conjunto de trabajo. El CLR generalmente asignará y liberará la memoria en fragmentos. El tamaño de estos varía de acuerdo con los detalles de implementación, pero debido a esto es casi imposible medir el uso de heap administrado en base a los contadores de memoria para el proceso.

Sin embargo, si nos fijamos en el uso real de memoria para los ejemplos, verá una diferencia.

Ejemplo 1

0:005>!dumpheap -stat 
... 
00b6911c  137   4500 System.String 
0016be60  8  480188  Free 
00b684c4  14  649184 System.Object[] 
Total 316 objects 
0:005> !eeheap -gc 
Number of GC Heaps: 1 
generation 0 starts at 0x01592dcc 
generation 1 starts at 0x01592dc0 
generation 2 starts at 0x01591000 
ephemeral segment allocation context: none 
segment begin allocated  size 
01590000 01591000 01594dd8 0x00003dd8(15832) 
Large object heap starts at 0x02591000 
segment begin allocated  size 
02590000 02591000 026a49a0 0x001139a0(1128864) 
Total Size 0x117778(1144696) 
------------------------------ 
GC Heap Size 0x117778(1144696) 

Ejemplo 2

0:006> !dumpheap -stat 
... 
00b684c4  14  649184 System.Object[] 
00b6911c 100137  2004500 System.String 
Total 100350 objects 
0:006> !eeheap -gc 
Number of GC Heaps: 1 
generation 0 starts at 0x0179967c 
generation 1 starts at 0x01791038 
generation 2 starts at 0x01591000 
ephemeral segment allocation context: none 
segment begin allocated  size 
01590000 01591000 0179b688 0x0020a688(2139784) 
Large object heap starts at 0x02591000 
segment begin allocated  size 
02590000 02591000 026a49a0 0x001139a0(1128864) 
Total Size 0x31e028(3268648) 
------------------------------ 
GC Heap Size 0x31e028(3268648) 

Como se puede ver a partir de la salida por encima de la segunda ejemplo no utilizar más memoria en el montón administrado.

0

Fuente:https://blogs.msdn.microsoft.com/ericlippert/2009/09/28/string-interning-and-string-empty/

cadena internación es una técnica de optimización por el compilador. Si tiene dos literales de cadena idénticos en una unidad de compilación, el código generado asegura que solo haya un objeto de cadena creado para toda la instancia de ese literal (caracteres entre comillas dobles) dentro del ensamblaje.

Ejemplo:

object obj = "Int32"; 
string str1 = "Int32"; 
string str2 = typeof(int).Name; 

salida de las siguientes comparaciones:

Console.WriteLine(obj == str1); // true 
Console.WriteLine(str1 == str2); // true  
Console.WriteLine(obj == str2); // false !? 

Note1: Los objetos se comparan por referencia.

Nota2: typeof (int) .Name se evalúa por el método de reflexión por lo que no se evalúa en tiempo de compilación. Aquí estas comparaciones se realizan en tiempo de compilación.

Análisis de los resultados:

  1. cierto porque ambos contienen misma literal y lo que el código generado tendrá un solo objeto hacer referencia a "Int32". Ver Nota 1.

  2. cierto porque el contenido de ambos el valor se comprueba que es la misma.

  3. falso porque str2 y obj no tienen el mismo literal. Ver Nota 2.

+1

No publique la misma respuesta a más de una pregunta. Si las preguntas son básicamente las mismas, [márquelas] (https://stackoverflow.com/privileges/flag-posts) como duplicados. De lo contrario, personalice la respuesta a la pregunta. –