В якій області пам'яті зберігається String Pool?
String Pool — це спеціальна область, де JVM зберігає унікальні рядки. Його розташування змінювалося в різних версіях Java.
🟢 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, якщо купа заповнена
Типові помилки
-
Помилка: Думати, що String Pool у Metaspace Рішення: Пул у Heap. Metaspace — лише для метаданих класів
-
Помилка: Не налаштовувати 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
-
GC і String Pool: У Java 7+ рядки з пулу МОЖУТЬ бути зібрані GC, якщо на них немає strong references. Але якщо ви зберігаєте посилання — вони не збираються.
-
Weak References у StringTable: Деякі JVM експериментували з weak references для записів StringTable, але у HotSpot використовуються strong references.
-
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]]