Pergunta

Estou tentando entender a string internadora e por que não parece funcionar no meu exemplo. O objetivo do exemplo é mostrar que o Exemplo 1 usa menos (muito menos memória), pois deve ter apenas 10 seqüências de caracteres na memória. No entanto, no código abaixo, ambos o exemplo, use aproximadamente a mesma quantidade de memória (tamanho virtual e conjunto de trabalho).

Por favor, aconselhe por que o Exemplo 1 não está usando muito menos memória? Obrigado

Exemplo 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();

Exemplo 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();
Foi útil?

Solução

De msdn Segundo, para estagiar uma string, você deve primeiro criar a string. A memória usada pelo objeto String ainda deve ser alocada, mesmo que a memória seja finalmente coletada de lixo.

Outras dicas

O problema é que o ToString () ainda alocará uma nova string, e depois estagie. Se o coletor de lixo não correr para coletar essas cordas "temporárias", o uso da memória será o mesmo.

Além disso, o comprimento de suas cordas é bem curto. 10.000 strings que são principalmente apenas um personagem é uma diferença de memória de cerca de 20kb que você provavelmente não vai notar. Tente usar cordas mais longas (ou um muito mais deles) e fazer um lixo coletar antes de verificar o uso da memória.

Aqui está um exemplo que faz mostre uma diferença:

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();
    }
}

Lembre -se de que o CLR gerencia a memória em nome do seu processo, por isso é realmente difícil descobrir que a pegada de memória gerenciada observa o tamanho virtual e o conjunto de trabalho. O CLR geralmente alocará e a memória livre em pedaços. O tamanho desses varia de acordo com os detalhes da implementação, mas, devido a isso, é quase impossível medir o uso de heap gerenciado com base nos contadores de memória para o processo.

No entanto, se você observar o uso de memória real para os exemplos, verá uma diferença.

Exemplo 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)

Exemplo 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 você pode ver na saída acima, o segundo exemplo usa mais memória na pilha gerenciada.

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

A internação da string é uma técnica de otimização do compilador. Se você tiver dois literais de sequência idênticos em uma unidade de compilação, o código gerado garante que exista apenas um objeto de string criado para toda a instância desse literal (caracteres fechados em cotações duplas) dentro da montagem.

Exemplo:

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

saída das seguintes comparações:

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

Nota 1: Os objetos são comparados por referência.

Nota 2: typeof (int) .name é avaliado pelo método de reflexão para que não seja avaliado no momento da compilação. Aqui essas comparações são feitas no momento da compilação.

Análise dos resultados:

  1. É verdade porque ambos contêm o mesmo literal e, portanto, o código gerado terá apenas um objeto referenciando "Int32". Veja a nota 1.

  2. verdadeiro porque o conteúdo de ambos o valor é verificado, o que é o mesmo.

  3. Falso porque STR2 e OBJ não têm o mesmo literal. Veja a nota 2.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top