Что такое пул строк (String Pool) в .NET?

«Что такое пул строк (String Pool) в .NET?» — вопрос из категории Управление памятью, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

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

Как это работает?

  1. Во время компиляции: Все строковые литералы, найденные в коде сборки, автоматически помещаются в пул.
  2. Во время выполнения: Создание строки с одинаковым литеральным значением приводит к возврату ссылки на уже существующий объект из пула.

Демонстрация:

string s1 = "Hello";
string s2 = "Hello";
string s3 = new string('H', 5); // "Hello", но создаётся через конструктор

// Сравнение ссылок
Console.WriteLine(ReferenceEquals(s1, s2)); // True: обе ссылаются на один объект в пуле
Console.WriteLine(ReferenceEquals(s1, s3)); // False: s3 — новый объект в куче

// Явное интернирование строки
string s4 = String.Intern(s3); // Принудительно помещаем s3 в пул
Console.WriteLine(ReferenceEquals(s1, s4)); // True: теперь s4 ссылается на объект из пула

// Проверка наличия в пуле
string interned = String.IsInterned("Hello"); // Вернёт "Hello" (ссылка на объект в пуле)
string notInterned = String.IsInterned(Guid.NewGuid().ToString()); // Вернёт null

Ключевые моменты и практические советы:

  • Что попадает в пул автоматически? Только строковые литералы из кода.
  • Динамические строки (результаты конкатенации, чтения из файла, ввода пользователя) НЕ интернируются автоматически. Для них нужно явно вызывать String.Intern().
  • Пул живёт в куче и не собирается GC до выгрузки домена приложения. Злоупотребление интернированием для уникальных динамических строк (например, GUID) может привести к утечке памяти.
  • Когда использовать String.Intern? Крайне редко. Возможный сценарий — обработка большого количества повторяющихся строковых значений, полученных динамически (например, парсинг XML/JSON с часто повторяющимися именами тегов). В 99% случаев доверяйте компилятору и CLR.
  • Сравнение строк: Благодаря пулу, сравнение строковых литералов через == (которое проверяет ссылки) может быть очень быстрым, но для надёжности всегда используйте String.Equals с указанием StringComparison (например, Ordinal).