Питання 18 · Розділ 13

Як працює String pool і як це пов'язано з незмінністю?

Якби рядки можна було змінювати, то зміна одного рядка вплинула б на всі інші, які посилаються на нього:

Мовні версії: English Russian Ukrainian

Junior Level

String Pool — це спеціальне сховище в пам’яті, де Java зберігає лише одну копію кожного унікального рядка.

String a = "Hello";
String b = "Hello";
System.out.println(a == b); // true — один і той самий рядок з пулу

Чому це працює тільки з незмінними рядками

Якби рядки можна було змінювати, то зміна одного рядка вплинула б на всі інші, які посилаються на нього:

// Гіпотетично — якби рядки були мутабельними
String a = "Hello";
String b = "Hello"; // те ж посилання з пулу
a.changeTo("World"); // зламає b теж!

Метод intern()

String s1 = new String("Hello"); // новий об'єкт в heap
String s2 = s1.intern();          // додає в пул, повертає посилання
String s3 = "Hello";              // з пулу
System.out.println(s2 == s3);     // true

Middle Level

Механізм роботи

  1. При компіляції — усі рядкові літерали потрапляють до таблиці констант класу
  2. При завантаженні — JVM перевіряє пул: якщо рядок є — повертає посилання, ні — створює новий
  3. Під час виконанняintern() додає рядок з heap в пул

Еволюція пам’яті

  • Java 6: String Pool був у PermGen (обмежений розмір, часті OOM)
  • Java 7u6+: String Pool переїхав з PermGen у звичайний Heap (керується GC)

Переваги

  • Економія пам’яті — дублікати рядків не створюються
  • Швидке порівняння — інтерновані рядки можна порівнювати через == за O(1) (одна інструкція CPU — порівняння посилань), тоді як equals() проходить по всіх символах.
  • Кешування hashCode — обчислюється один раз

Коли використовувати intern()

  • Багато повторюваних рядків (ключі, константи конфігурації)
  • Ключі в HashMap з мільярдами записів (економія на дублікатах)

Senior Level

Зв’язок з незмінністю

Незмінність — необхідна умова для String Pool. Патерн Flyweight на рівні мови працює лише тому, що Java гарантує: вміст String ніколи не зміниться.

Продуктивність

  • Порівняння через == для інтернованих рядків — O(1), але в бізнес-логіці все одно використовуйте equals()
  • Кешований hashCode прискорює роботу HashMap — повторні lookup’и не вимагають перерахунку

Ризики intern()

  • Memory leak — рядки з пулу не видаляються GC, доки живий ClassLoader
  • PermGen/Heap OOM — занадто багато унікальних рядків через intern() може переповнити пам’ять
  • В Java 7+ пул в Heap, але рядки все ще можуть жити дуже довго

Резюме для Senior

  • String Pool — Flyweight патерн на рівні мови
  • Працює лише завдяки гарантії незмінності
  • Пул в Heap (Java 7+), керується GC
  • intern() — потужний, але небезпечний інструмент: слідкуйте за обсягом унікальних рядків

Шпаргалка для інтерв’ю

Обов’язково знати:

  • String Pool — сховище унікальних рядків, працює ТІЛЬКИ завдяки незмінності (Flyweight)
  • Літерали потрапляють в пул при завантаженні класу, intern() додає рядок з heap
  • Java 6: пул в PermGen (часті OOM); Java 7+: пул в Heap (керується GC)
  • Порівняння інтернованих рядків через == — O(1), але в бізнес-логізі використовуйте equals()
  • String кешує hashCode — обчислюється один раз, прискорює HashMap
  • Ризики intern(): memory leak, Heap OOM при занадто великій кількості унікальних рядків

Часті уточнювальні запитання:

  • Чому пул неможливий без незмінності? — Зміна одного рядка зламає всі посилання на нього
  • Коли використовувати intern()? — Багато повторюваних рядків, ключі в величезних HashMap
  • intern() — це безпечно? — Рядки з пулу не видаляються GC, доки живий ClassLoader — risk of OOM
  • Compact Strings впливають на пул? — Ні, пул працює незалежно; Compact Strings економлять 50% пам’яті

Червоні прапорці (НЕ говорити):

  • «String Pool в PermGen» — з Java 7+ в Heap
  • «intern() завжди прискорює» — при зловживанні викликає OOM
  • «Порівнювати рядки через == нормально після intern()» — в бізнес-логізі завжди equals()
  • «Пул копіює рядки» — літерали поміщаються в пул при завантаженні класу

Пов’язані теми:

  • [[4. Чому клас String є незмінним]]
  • [[5. Які наслідки незмінності String]]
  • [[19. Чи можна змінити значення String через рефлексію]]
  • [[27. Чи можна використовувати незмінні об’єкти як ключі в HashMap]]