Питання 11 · Розділ 12

В якій області пам'яті зберігається String Pool?

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

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

🟢 Junior Level

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

Коротка історія:

  • Java 6 і раніше: PermGen (спеціальна область для метаданих) — часто виникала помилка OutOfMemoryError: PermGen space
  • Java 7+: Java Heap (звичайна купа, де живуть усі об’єкти) — тепер GC може видаляти невикористовувані рядки з пулу

Чому це важливо: У сучасній Java String Pool знаходиться у Heap, тому він обмежений загальним розміром пам’яті (-Xmx), а не окремим лімітом.

Проста аналогія: PermGen — як маленький сейф з фіксованим розміром. Heap — як великий склад, який можна розширити.

Практичний зміст: у Java 7+ ви можете інтернувати більше рядків без окремого налаштування, але тепер пул конкурує за ту ж пам’ять, що і ваші об’єкти.

Коли розташування String Pool не має значення

Для 95% додатків це академічне знання. Знання важливе при: (1) масовому intern(), (2) тюнінгу GC, (3) міграції між версіями Java.


🟡 Middle Level

Деталі по версіях

Java 6: PermGen

  • PermGen — окрема область пам’яті для метаданих класів
  • Розмір обмежений (-XX:MaxPermSize, зазвичай 64-256MB)
  • GC у PermGen працював неефективно
  • Надмірне intern()OOM: PermGen space

Java 7 (точніше, 7u6): String Pool переміщено з PermGen у Java Heap.

  • Переміщено до основної купи
  • String Pool ділить пам’ять з усіма об’єктами додатку
  • GC ефективно видаляє рядки без посилань
  • Обмежений лише -Xmx

Java 8+: Metaspace (НЕ String Pool!)

  • PermGen замінено на Metaspace (у Native Memory)
  • String Pool залишився у Java Heap (не в Metaspace!)
  • Поширена помилка — думати, що пул переїхав у Metaspace

Практичне застосування

// У Java 7+ можна сміливо інтернувати більше рядків
for (String city : cities) {
    city = city.intern(); // Не викличе OOM: PermGen
}
// Але може викликати OOM: Java heap space, якщо купа заповнена

Типові помилки

  1. Помилка: Думати, що String Pool у Metaspace Рішення: Пул у Heap. Metaspace — лише для метаданих класів

  2. Помилка: Не налаштовувати StringTableSize при масовому intern() Рішення: -XX:StringTableSize=1000003


🔴 Senior Level

Internal Implementation

Структура пам’яті JVM (Java 8+):

JVM Memory Layout:
├── Heap
│   ├── Young Generation (Eden, S0, S1)
│   ├── Old Generation
│   └── String Pool (StringTable) ← ТУТ
├── Metaspace (Native Memory)
│   └── Class metadata, method data
└── Code Cache
    └── JIT compiled code

StringTable:

// OpenJDK — StringTable — нативна хеш-таблиця у C++
class StringTable : public RehashableHashtable<oop, mtSymbol> {
  // oop (ordinary object pointer) — внутрішній тип JVM для посилання на Java-об'єкт.
  // mtSymbol — категорія пам'яті у JVM (Symbol = рядкові дані).
  // Зберігається у C++ heap, але самі String об'єкти — у Java Heap
};

Еволюція — деталі переїзду

Java 6 (PermGen):

  • Рядки у пулу жили вічно (або до OOM)
  • GC не чистив PermGen ефективно
  • -XX:MaxPermSize=256m — жорсткий ліміт

Java 7 (переїзд у Heap):

  • JEP: переміщення String Table у Java Heap
  • Причина: PermGen видалявся у майбутніх версіях
  • Рядки стали звичайними об’єктами для GC

Java 8 (Metaspace):

  • PermGen видалено повністю
  • Metaspace — Native Memory, авто-розширення
  • String Pool не переїхав — залишився у Heap

Архітектурні Trade-offs

Пул у Heap — плюси:

  • GC керує рядками як звичайними об’єктами
  • Немає окремого ліміту — обмежений загальним -Xmx
  • String entries без посилань можуть бути зібрані GC

Пул у Heap — мінуси:

  • Велика кількість інтернованих рядків конкурує за Heap з бізнес-об’єктами
  • Може збільшити час GC pause (більше об’єктів для сканування)
  • G1 GC: StringTable scan додає overhead до evacuation pause

Edge Cases

  1. GC і String Pool: У Java 7+ рядки з пулу МОЖУТЬ бути зібрані GC, якщо на них немає strong references. Але якщо ви зберігаєте посилання — вони не збираються.

  2. Weak References у StringTable: Деякі JVM експериментували з weak references для записів StringTable, але у HotSpot використовуються strong references.

  3. String Deduplication vs Pool: Дедуплікація (G1 GC) об’єднує byte[] масиви у Heap, але це окремий механізм від String Pool.

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

| Метрика | PermGen (Java 6) | Heap (Java 7+) | | ————— | ————————– | ———————— | | Max size | -XX:MaxPermSize (64-256MB) | -Xmx (GB+) | | GC efficiency | Poor | Good | | String eviction | Manual (restart) | Automatic (GC) | | OOM risk | High (PermGen space) | Medium (Java heap space) |

Production Experience

Сценарій: Міграція додатку з Java 8 на Java 17:

  • Команда думала, що String Pool у Metaspace
  • Налаштували -XX:MaxMetaspaceSize=512m, але не збільшили -Xmx
  • Результат: OOM: Java heap space при навантаженні
  • Fix: збільшили -Xmx з 2g до 4g, пул запрацював коректно

Monitoring

# Перевірити розмір StringTable
jcmd <pid> VM.stringtable -verbose

# Memory usage
jstat -gc <pid> 1000

# Heap dump аналіз
jmap -dump:live,format=b,file=heap.hprof <pid>
# В MAT: java.lang.String → dominator tree

Best Practices для Highload

  • String Pool у Heap → враховуйте його при розрахунку -Xmx
  • При масовому intern(): -XX:StringTableSize=1000003 (або більше)
  • Моніторьте через jcmd VM.stringtable
  • Для автоматичної дедуплікації: -XX:+UseStringDeduplication (G1 GC)

🎯 Шпаргалка для співбесіди

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

  • Java 6 і нижче: String Pool у PermGen (фіксований розмір, часті OOM)
  • Java 7+: String Pool переміщено у Java Heap (керується GC, обмежений -Xmx)
  • Java 8+: Metaspace замінив PermGen, але String Pool залишився у Heap (НЕ в Metaspace!)
  • У Heap GC може видаляти рядки з пулу, якщо на них немає посилань
  • Поширена помилка — думати, що пул у Metaspace
  • StringTable — нативна хеш-таблиця у C++, але об’єкти String — у Java Heap

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

  • Чому перенесли String Pool з PermGen у Heap? — PermGen видалявся, GC у PermGen працював неефективно, у Heap рядки стали звичайними об’єктами для GC.
  • Чи може String Pool викликати OOM? — Так, OOM: Java heap space у Java 7+ (раніше OOM: PermGen space).
  • У якій області пам’яті String Pool у Java 17? — У Java Heap. Не в Metaspace, не в Code Cache.
  • Як моніторити String Pool?jcmd <pid> VM.stringtable -verbose.

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

  • ❌ “String Pool у Metaspace” — найчастіша помилка, пул у Heap
  • ❌ “String Pool має окремий ліміт пам’яті” — обмежений загальним -Xmx
  • ❌ “GC не чистить String Pool” — у Java 7+ може видаляти unreachable записи
  • ❌ “PermGen і Metaspace — одне й те саме” — Metaspace у Native Memory, PermGen був у Heap

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

  • [[1. Як працює String Pool]]
  • [[12. Чи може String Pool викликати OutOfMemoryError]]
  • [[3. Коли потрібно використовувати intern()]]
  • [[22. Що таке String deduplication в G1 GC]]