我正在尝试理解字符串实习,为什么在我的示例中似乎不起作用。示例的要点是显示示例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();
答案 0 :(得分:16)
问题是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();
}
}
答案 1 :(得分: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 :(得分:2)
从msdn 秒,到实际的字符串,您必须先创建字符串。必须仍然分配String对象使用的内存,即使内存最终将被垃圾回收。
答案 3 :(得分: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 :通过引用比较对象。
Note2 :typeof(int).Name由反射方法计算,因此在编译时不会对其进行求值。这些比较是在编译时进行的。
结果分析:
是的,因为它们都包含相同的文字和代码 生成的只有一个对象引用“Int32”。 请参阅注释 1。
是的,因为检查两个值的内容是相同的。
false,因为str2和obj没有相同的文字。 查看 注2: