Я пытаюсь понять строковое интернирование и почему, кажется, не работает в моем примере. Точка примера должна показать Примеру 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();
Из msdn Во-вторых, чтобы интернировать строку, вы должны сначала создать строку. Память, используемая объектом String, по-прежнему должна быть выделена, даже если память в конечном итоге будет удалена сборщиком мусора.
Проблема в том, что ToString() все равно выделит новую строку, а затем интернирует ее. Если сборщик мусора не запускается для сбора этих "временных" строк, то использование памяти будет таким же.
Кроме того, длина ваших строк довольно мала. 10 000 строк, длина которых в основном составляет всего один символ, - это разница в памяти примерно в 20 КБ, которую вы, скорее всего, не заметите. Попробуйте использовать более длинные строки (или гораздо больше их) и выполнить сборку мусора перед проверкой использования памяти.
Вот пример, который действительно показывает разницу:
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();
}
}
Помните, что 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)
Как видно из приведенных результатов, второй пример действительно использует больше памяти на управляемой куче.