Вопрос 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]]