← Назад к вопросам

Является ли String Pool частью heap?

2.0 Middle🔥 201 комментариев
#JVM и управление памятью

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI21 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

String Pool и Heap память

Да, String Pool является частью Heap, но это важный нюанс, который часто неправильно понимают. Расскажу детально.

Краткий ответ

Да, String Pool находится в Heap, но это специальная область с отдельным управлением.

История String Pool

В Java 6 и ранее:

  • String Pool находился в PermGen (Permanent Generation)
  • PermGen — это отдельная область памяти для метаданных класса
  • Размер ограничен и редко увеличивается

В Java 7 и позже:

  • String Pool перемещён в Heap
  • PermGen переименован в Metaspace
  • Это сделано для автоматической очистки неиспользуемых String'ов

Структура памяти в Java

Runtime Memory
├── Stack (локальные переменные, ссылки)
├── Heap
│   ├── Young Generation
│   │   ├── Eden
│   │   ├── Survivor 0
│   │   └── Survivor 1
│   │
│   ├── Old Generation
│   │
│   └── String Pool (ТАКЖЕ В HEAP)
│       └── Интернированные String'ы
│
└── Metaspace (Java 8+)
    └── Метаданные класса, константы

Что такое String Pool?

String Pool — это **кеш интернированных строк**:

String s1 = "Hello";     // Создаётся в String Pool
String s2 = "Hello";     // Ссылается на тот же объект в Pool
String s3 = new String("Hello");  // Новый объект в обычной части Heap

system.out.println(s1 == s2);  // true (одна и та же ссылка)
system.out.println(s1 == s3);  // false (разные объекты)
system.out.println(s1.equals(s3));  // true (одинаковое содержимое)

Механизм интернирования

String literals (строковые литералы): Автоматически помещаются в String Pool при загрузке класса:

public class StringExample {
    public static void main(String[] args) {
        // Все эти строки автоматически интернируются
        String s1 = "Hello";
        String s2 = "World";
        String s3 = "Hello";  // Указывает на тот же объект, что s1
    }
}

// Примерно так это выглядит в памяти:
// StringPool {
//   "Hello" -> объект с id=1000
//   "World" -> объект с id=2000
// }
// s1 и s3 оба указывают на объект id=1000

Явное интернирование:

String s1 = new String("Hello");  // Новый объект в Heap, НЕ в Pool
String s2 = s1.intern();          // Добавить в Pool
String s3 = "Hello";              // Из Pool

system.out.println(s1 == s2);  // false (s1 в обычном Heap)
system.out.println(s2 == s3);  // true (оба из Pool)

Почему String Pool в Heap (Java 7+)?

Преимущества:

  1. Автоматическая очистка:
if (someCondition) {
    String s = new String("temp").intern();
    // После выхода из scope, если s никто не хранит,
    // GC может удалить объект из Pool
}

Без этого в PermGen:

  • String'ы скапливались и никогда не удалялись
  • OutOfMemoryError: PermGen space была частой проблемой
  1. Единое управление памятью:
  • GC может обрабатывать String Pool как часть Heap
  • Нет отдельной логики для PermGen
  1. Динамический размер:
  • Размер Heap легко настраивается (-Xmx, -Xms)
  • PermGen было сложнее настраивать

Размер String Pool

Начальный размер:

# По умолчанию примерно 60013 bucket'ов
# Можно настроить через JVM опцию
java -XX:StringTableSize=100000 MyApp

Примечание: StringTableSize — это размер hash table, не кол-во String'ов.

Мониторинг String Pool

С помощью JVM метрик:

import com.sun.management.HotSpotInternalMonitoringMBean;
import java.lang.management.ManagementFactory;

public class StringPoolMonitor {
    public static void main(String[] args) {
        // В Java 9+ используй jdk.management.jfr
        // В Java 8 и ранее можно использовать JVM options
        
        System.out.println("Max memory: " + Runtime.getRuntime().maxMemory());
        System.out.println("Total memory: " + Runtime.getRuntime().totalMemory());
        System.out.println("Free memory: " + Runtime.getRuntime().freeMemory());
    }
}

С помощью jstat:

jstat -gc <pid>
# Покажет размер каждого поколения Heap'а

Проблемы, связанные с String Pool

1. OutOfMemory из-за String Pool (старый способ):

// Это могло привести к OutOfMemory в Java 6
public class StringPoolProblem {
    public static void main(String[] args) {
        for (int i = 0; i < 1_000_000; i++) {
            String s = new String("string" + i).intern();
            // Каждый String интернируется и добавляется в Pool
            // В PermGen это вызывало OutOfMemory
        }
    }
}

// В Java 7+ это всё ещё может быть проблемой, если String'ов очень много
// но GC может их удалить

2. Утечка памяти через intern():

public class StringLeakExample {
    private List<String> strings = new ArrayList<>();
    
    public void process(String[] input) {
        for (String s : input) {
            // Опасно интернировать пользовательский ввод!
            strings.add(s.intern());
            // Если String'ы в list живут долго, они не будут удалены из Pool
        }
    }
}

Best Practices

1. Не переусложняй с intern():

// ❌ Плохо — без необходимости интернируешь
String s = userInput.intern();

// ✅ Хорошо — используй intern только когда нужна экономия памяти
if (knownStrings.contains(s)) {
    s = s.intern();  // Экономим память для известных строк
}

2. Понимай когда String Pool помогает:

// ✅ Хорошо — литералы автоматически интернируются
if (method.equals("GET")) {  // "GET" автоматически в Pool
    // ...
}

if (status.equals("ACTIVE")) {  // "ACTIVE" автоматически в Pool
    // ...
}

// Эти сравнения эффективны благодаря String Pool

3. Настройка размера (если нужно):

# Если приложение работает с большим кол-вом unique String'ов
java -XX:StringTableSize=200000 MyApplication

Визуализация в памяти

JVM Memory Layout в Java 7+:

Stack:                      Heap:
┌─────────────────┐        ┌──────────────────────────┐
│ s1 → ref→1001   │───┐    │ Heap Objects             │
│ s2 → ref→1001   │   │    │                          │
│ s3 → ref→1002   │   │    │ Object 1001: "Hello"     │ ← String Pool
│ s4 → ref→1003   │   │    │ Object 1002: "Hello"     │ ← Regular Heap
└─────────────────┘   │    │ Object 1003: "World"     │ ← Regular Heap
                      │    │ ...                      │
                      └───→ [String Pool (часть Heap)]│
                           └──────────────────────────┘

s1 и s2 указывают на один объект в Pool (1001)
s3 указывает на копию в обычном Heap (1002)
s4 указывает на другой объект

Заключение

  1. String Pool находится в Heap (с Java 7+)
  2. Раньше был в PermGen (Java 6 и ранее)
  3. Автоматически интернируются строковые литералы
  4. Можно явно интернировать через .intern()
  5. GC может удалять неиспользуемые интернированные строки (в отличие от PermGen)
  6. Будь осторожен с intern() на пользовательском вводе
  7. String Pool помогает экономить память для часто повторяющихся строк
Является ли String Pool частью heap? | PrepBro