11

私は文字列のインターンを理解しようとしていますが、私の例ではなぜうまくいかないようです。この例のポイントは、例1ではメモリに10個の文字列しかないため、使用するメモリが少ない(メモリがはるかに少ない)ことを示しています。ただし、以下のコードでは、両方の例でほぼ同じ量のメモリ(仮想サイズとワーキングセット)を使用しています。

例1があまりメモリを使用していない理由を教えてください。ありがとう

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

例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();
4

4 に答える 4

17

問題は、ToString()が新しい文字列を割り当ててから、それをインターンすることです。ガベージコレクタがこれらの「一時的な」文字列を収集するために実行されない場合、メモリ使用量は同じになります。

また、弦の長さはかなり短いです。ほとんどが1文字の長さの10,000文字列は、約20KBのメモリの違いであり、おそらく気付かないでしょう。メモリ使用量を確認する前に、より長い文字列(またはより多くの文字列)を使用してガベージコレクションを実行してみてください。

違いを示す例を次に示します。

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();
    }
}
于 2010-03-24T09:51:20.823 に答える
7

CLR はプロセスに代わってメモリを管理するため、仮想サイズとワーキング セットからマネージド メモリのフットプリントを把握するのは非常に困難です。CLR は通常、メモリをチャンク単位で割り当てて解放します。これらのサイズは実装の詳細によって異なりますが、このため、プロセスのメモリ カウンターに基づいてマネージド ヒープの使用量を測定することはほぼ不可能です。

ただし、例の実際のメモリ使用量を見ると、違いがわかります。

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

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

上記の出力からわかるように、2 番目の例ではマネージ ヒープでより多くのメモリが使用されます。

于 2010-03-24T10:08:13.347 に答える
2

msdn から 次に、文字列をインターンするには、最初に文字列を作成する必要があります。メモリが最終的にガベージ コレクションされる場合でも、String オブジェクトによって使用されるメモリは引き続き割り当てられる必要があります。

于 2010-03-24T09:56:16.407 に答える