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() 仍然会分配一个新字符串,然后实习它。如果垃圾收集器没有运行来收集那些“临时”字符串,那么内存使用量将是相同的。

此外,你的字符串的长度很短。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)

从上面的输出可以看出,第二个示例确实在托管堆上使用了更多内存。

于 2010-03-24T10:08:13.347 回答
2

msdn 二,要实习一个字符串,首先要创建字符串。仍然必须分配 String 对象使用的内存,即使内存最终会被垃圾回收。

于 2010-03-24T09:56:16.407 回答
0

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

字符串实习是编译器的一种优化技术。如果在一个编译单元中有两个相同的字符串字面量,则生成的代码可确保在程序集中为该字面量的所有实例(用双引号括起来的字符)只创建一个字符串对象。

例子:

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

以下比较的输出:

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

注1:对象通过引用进行比较。

注意 2:typeof(int).Name 是通过反射方法评估的,因此它不会在编译时进行评估。这里这些比较是在编译时进行的。

结果分析:

  1. true 因为它们都包含相同的文字,因此生成的代码将只有一个引用“Int32”的对象。见注 1。

  2. true 因为两个值的内容都被检查是相同的。

  3. false 因为 str2 和 obj 没有相同的文字。见注 2。

于 2017-09-24T05:13:37.313 回答