← Назад к вопросам

Что такое интернирование строк?

1.0 Junior🔥 141 комментариев
#Основы C# и .NET

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI7 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое интернирование строк?

Интернирование строк (string interning) — это механизм оптимизации в C# (и других языках платформы .NET), при котором все идентичные строковые литералы компилятором и средой выполнения сохраняются в специальной области памяти, называемой пулом интернированных строк (intern pool), с целью экономии памяти и ускорения сравнения строк.

Основная идея и принцип работы

Поскольку строки в C# являются неизменяемыми (immutable), безопасно хранить одну копию строки в памяти и ссылаться на неё из нескольких мест. Пусть у нас есть код:

string s1 = "Hello";
string s2 = "Hello";
string s3 = "Hel" + "lo"; // конкатенация литералов, вычисляется на этапе компиляции

В этом случае:

  • Компилятор C# на этапе компиляции помещает литерал "Hello" в пул интернированных строк сборки (в метаданные).
  • Во время выполнения все переменные s1, s2 и s3 будут ссылаться на один и тот же объект в памяти в куче, а не создавать отдельные экземпляры.
  • Это подтверждается сравнением ссылок:
Console.WriteLine(object.ReferenceEquals(s1, s2)); // True
Console.WriteLine(object.ReferenceEquals(s1, s3)); // True

Пул интернированных строк в .NET

Пул интернированных строк делится на две части:

  • Пул на уровне сборки (Assembly Intern Pool): строковые литералы из кода компилируются в метаданные сборки и автоматически интернируются.
  • Пул на уровне процесса (Process-Wide Intern Pool): управляется CLR, используется для интернирования строк во время выполнения, например, через String.Intern().

Методы для управления интернированием

Класс System.String предоставляет статические методы для работы с пулом:

  1. String.Intern(string str) — возвращает ссылку на интернированную версию строки str. Если строка уже есть в пуле — возвращает существующую ссылку; если нет — добавляет её в пул и возвращает ссылку.
  2. String.IsInterned(string str) — проверяет, интернирована ли строка str. Возвращает ссылку на интернированную строку или null, если её нет в пуле.

Пример использования:

string s4 = new string('A', 3); // "AAA" создаётся в куче, не интернируется автоматически
string s5 = "AAA";
Console.WriteLine(object.ReferenceEquals(s4, s5)); // False

string s6 = String.Intern(s4);
Console.WriteLine(object.ReferenceEquals(s5, s6)); // True, теперь s6 ссылается на интернированную "AAA"

Когда строки интернируются автоматически?

  • Строковые литералы в коде (как в примере выше).
  • Константные выражения, вычисляемые на этапе компиляции:
    const string part = "World";
    string constantExpr = "Hello " + part; // скомпилируется как "Hello World", будет интернировано
    
  • Атрибуты с строковыми параметрами.

Когда строки НЕ интернируются автоматически?

  • Строки, созданные во время выполнения (например, через new string(...), чтение из файла, ввод пользователя).
  • Динамическая конкатенация:
    string s7 = "He";
    string s8 = s7 + "llo"; // не интернируется автоматически
    Console.WriteLine(object.ReferenceEquals(s1, s8)); // False
    

Преимущества интернирования

  1. Экономия памяти: вместо хранения множества копий одинаковых строк хранится одна.
  2. Ускорение сравнения строк: если строки интернированы, можно сравнивать их по ссылке (ReferenceEquals) вместо поэлементного сравнения, что особенно полезно для операций, часто использующих одинаковые строки (например, ключи в словарях).

Недостатки и предостережения

  • Пул интернированных строк живёт до выгрузки домена приложения (AppDomain), поэтому интернирование динамически созданных строк без ограничений может привести к утечке памяти (пул будет расти).
  • Избыточное интернирование может замедлить работу из-за накладных расходов на поиск в пуле.
  • Сравнение по ссылке для неинтернированных строк даёт неверные результаты, поэтому важно использовать его осознанно.

Практическое применение

Интернирование полезно в сценариях с множеством повторяющихся строк, например:

  • Парсинг CSV/XML/JSON, где одни и те же имена полей встречаются многократно.
  • Хранение ключей в словарях, где ключи часто повторяются.
  • Оптимизация работы с перечислениями (enum) или константами.

Пример оптимизации словаря:

var dictionary = new Dictionary<string, int>(StringComparer.Ordinal);
// Если ключи интернированы, сравнение будет быстрее
string key = String.Intern(ReadKeyFromNetwork());
dictionary[key] = 42;

Вывод

Интернирование строк — это мощный, но требующий осторожности инструмент оптимизации, встроенный в платформу .NET. Оно автоматически применяется к строковым литералам, но для динамических строк требует ручного управления через String.Intern(). Правильное использование интернирования может значительно улучшить производительность и снизить потребление памяти в приложениях, активно работающих со строками.