Почему по умолчанию сохраняются только буквенные строки в пуле стажеров? - PullRequest
29 голосов
/ 14 декабря 2011

Почему по умолчанию в пуле интернов сохраняются только литеральные строки?

Пример из MSDN :

String s1 = "MyTest";
String s2 = new StringBuilder().Append("My").Append("Test").ToString(); 
String s3 = String.Intern(s2); 
Console.WriteLine("s1 == '{0}'", s1);
Console.WriteLine("s2 == '{0}'", s2);
Console.WriteLine("s3 == '{0}'", s3);
Console.WriteLine("Is s2 the same reference as s1?: {0}", (Object)s2==(Object)s1); 
Console.WriteLine("Is s3 the same reference as s1?: {0}", (Object)s3==(Object)s1);

/*
This example produces the following results:
s1 == 'MyTest'
s2 == 'MyTest'
s3 == 'MyTest'
Is s2 the same reference as s1?: False
Is s3 the same reference as s1?: True
*/

Ответы [ 3 ]

57 голосов
/ 14 декабря 2011

Короткий ответ: интернирование буквенных строк стоит дешево во время выполнения , а экономит память . Служебные не-литеральные строки стоят дорого во время выполнения и, следовательно, экономит небольшое количество памяти в обмен на замедление общих случаев .

Стоимость "оптимизации" интернирующих строк во время выполнения не окупается, и поэтому фактически не является оптимизацией. Стоимость интернирования буквальных строк дешевая и поэтому окупается.

Я отвечу на ваш вопрос более подробно здесь:

http://blogs.msdn.com/b/ericlippert/archive/2009/09/28/string-interning-and-string-empty.aspx

23 голосов
/ 14 декабря 2011

Разработчики языка решили, что затраты на интернирование каждого промежуточного строкового значения не стоят затрат производительности. Для интернирования коллекционируемых строк мусора требуется одна глобальная слабая карта, которая может стать узким местом при большом количестве потоков.

3 голосов
/ 14 декабря 2011

Стажирующие строки почти не дадут пользы в большинстве сценариев использования строк, даже если у них будет пул интернинга со слабой ссылкой нулевой стоимости (идеальная реализация интернирования). Чтобы интернирование строк могло принести какую-либо пользу, необходимо, чтобы несколько ссылок на совпадающие строки совпадали в течение достаточно «длительного» времени.

Рассмотрим следующие две программы:

  1. Введите 100 000 строк из текстового файла, каждая из которых содержит произвольный текст, а затем 100 000 пятизначных чисел. Рассматривайте каждое число, считываемое в виде индекса с нуля, в список из 100 000 строк, которые были прочитаны, и выводите соответствующую строку на выход.
  2. Введите 100 000 строк из текстового файла, выводя каждую строку, содержащую последовательность символов «fnord».

Для первой программы, в зависимости от содержимого текстового файла, интернирование строк может привести к почти 50 000: 1 экономии памяти (если строка содержит 100 000 идентичных длинных строк текста) или может представлять собой общую потерю (если все 100 000 строк разные). При отсутствии интернирования строк входной файл с 100 000 одинаковых строк приведет к тому, что 100 000 действующих экземпляров одной и той же строки будут существовать одновременно . Благодаря интернированию строк количество живых экземпляров может быть уменьшено до двух. Конечно, компилятор не может даже попытаться угадать, может ли входной файл содержать 100 000 одинаковых строк, 100 000 различных строк или что-то промежуточное.

Для второй программы маловероятно, что даже идеальная реализация интернирования строк принесет много пользы. Даже если все 100 000 строк входного файла оказались идентичными, интернирование не могло сэкономить много памяти. Эффект интернирования заключается не в том, чтобы предотвратить создание избыточных экземпляров строк, а в том, чтобы позволить идентифицировать и отбрасывать избыточные экземпляры строк. Так как каждая строка может быть отброшена после того, как она будет проверена и либо выведена, либо нет, единственная вещь, которую интернирование может купить, - это (теоретическая) способность отбрасывать избыточные экземпляры строк (очень) немного раньше, чем было бы возможно в противном случае.

В некоторых случаях может быть полезно кэширование определенных «промежуточных» строковых результатов, но эту задачу действительно лучше оставить программисту. Например, у меня есть программа, которая должна преобразовывать много байтов в двузначные шестнадцатеричные строки. Чтобы облегчить это, у меня есть массив из 255 строк, которые содержат строковые эквиваленты значений от 00 до FF. Я знаю, что в среднем каждая строка в этом массиве будет использоваться, как минимум, сотни или тысячи раз, поэтому кэширование этих строк - огромный выигрыш. С другой стороны, строки могут быть кэшированы только потому, что я знаю, что они представляют. Я могу знать, что для любого n 0-255 String.Format("{0:X2}",n) всегда будет давать одно и то же значение, но я бы не ожидал, что компилятор это знает.

...