← Назад к вопросам
Где могут храниться строки в памяти в Java?
2.0 Middle🔥 201 комментариев
#JVM и управление памятью#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Где хранятся строки в памяти в Java
Строки в Java могут храниться в двух основных местах: String Pool (String Interning Pool) в памяти, которая изолирована от обычного Heap, и в самом Heap.
String Pool — специальная область памяти
String Pool — это специальная память для хранения интернированных строк. Это оптимизация Java для экономии памяти, так как одинаковые строки хранятся один раз.
Строка в String Pool (до Java 7)
// До Java 7: String Pool находился в PermGen
String s1 = "Hello";
String s2 = "Hello";
system.out.println(s1 == s2); // true (одна и та же строка в PermGen)
Строка в String Pool (Java 7+)
// Java 7+: String Pool переместился в Heap
String s1 = "Hello"; // Создает в String Pool (часть Heap)
String s2 = "Hello"; // Находит в String Pool, не создает новую
String s3 = new String("Hello"); // Создает в Heap (вне Pool)
system.out.println(s1 == s2); // true (обе ссылаются на одну строку)
system.out.println(s1 == s3); // false (разные объекты)
system.out.println(s1.equals(s3)); // true (одинаковое содержание)
Визуализация памяти для строк
Java 7+ Памяти:
Heap:
┌─────────────────────────────────────────────────┐
│ Heap (GC) │
│ ┌────────────────────────────────────────┐ │
│ │ String Pool (String interning) │ │
│ │ ┌──────────────────┐ │ │
│ │ │ "Hello" -> obj1 │ (одна копия) │ │
│ │ └──────────────────┘ │ │
│ └────────────────────────────────────────┘ │
│ │
│ Обычные объекты: │
│ ┌──────────────────┐ │
│ │ "Hello" -> obj2 │ (другой объект) │
│ └──────────────────┘ │
│ ┌──────────────────┐ │
│ │ "World" -> obj3 │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────┘
Stack:
┌─────────────────┐
│ s1 -> obj1 │ (ссылка на String Pool)
│ s2 -> obj1 │ (ссылка на String Pool)
│ s3 -> obj2 │ (ссылка на Heap)
└─────────────────┘
Способы создания строк
1. Литерал строки → String Pool
String s = "Hello"; // Автоматически в String Pool
// При компиляции Java видит:
// - Является ли "Hello" уже в Pool?
// - Если да → используй существующую
// - Если нет → добавь в Pool
2. Оператор new → обычный Heap
String s = new String("Hello");
// Процесс:
// 1. Проверяет String Pool на "Hello"
// - Если есть, запоминает ссылку
// - Если нет, добавляет в Pool
// 2. Создает НОВЫЙ объект в Heap (вне Pool)
// 3. s ссылается на объект в Heap, а не Pool
String s1 = "Hello";
String s2 = new String("Hello");
System.out.println(s1 == s2); // false (разные объекты)
System.out.println(s1.equals(s2)); // true (одинаковое содержание)
3. Конкатенация строк → Heap
String s1 = "Hello";
String s2 = "Hello";
String s3 = s1 + s2; // Результат в Heap (вне Pool)
System.out.println(s3 == "HelloHello"); // false
// Почему? Компилятор оптимизирует только литералы:
String optimized = "Hello" + "Hello"; // Компилятор делает → "HelloHello"
system.out.println(optimized == "HelloHello"); // true
4. intern() → String Pool
String s1 = new String("Hello"); // Heap
String s2 = s1.intern(); // Добавляет в Pool и возвращает ссылку
System.out.println(s1 == s2); // false (s1 → Heap, s2 → Pool)
System.out.println(s2 == "Hello"); // true (Pool)
// Использование intern для кэширования:
String city = getUserCity().intern(); // Кэшируем часто используемые значения
Примеры в разных сценариях
Сценарий 1: Читаются строки из файла
BufferedReader reader = new BufferedReader(new FileReader("file.txt"));
String line;
while ((line = reader.readLine()) != null) {
// line находится в Heap (вне Pool)
// Каждая строка из файла создается как новый объект
// Если нужна экономия памяти для повторяющихся значений:
line = line.intern(); // Добавит в Pool
}
Сценарий 2: Разные операции со строками
public void stringOperations() {
// 1. Литерал → Pool
String s1 = "java";
// 2. new → Heap
String s2 = new String("java");
// 3. substring → Heap (не в Pool по умолчанию)
String s3 = s1.substring(0, 2); // "ja" → Heap
// 4. toLowerCase → Heap
String s4 = "JAVA".toLowerCase(); // "java" → Heap
// 5. Конкатенация → Heap
String s5 = "java" + "script"; // → Heap
// 6. format → Heap
String s6 = String.format("Hello %s", "world"); // → Heap
// 7. join → Heap
String s7 = String.join("-", "a", "b", "c"); // → Heap
}
Сценарий 3: Сравнение строк
String a = "hello";
String b = "hello";
String c = new String("hello");
String d = c.intern();
// Где они находятся?
// a → Pool
// b → Pool (та же ссылка, что и a)
// c → Heap
// d → Pool
System.out.println(a == b); // true (Pool)
System.out.println(a == c); // false (Pool vs Heap)
System.out.println(a == d); // true (Pool)
System.out.println(a.equals(c)); // true (содержание)
Проблемы с String Pool
1. OutOfMemoryError
// Если добавлять слишком много строк в Pool через intern()
for (int i = 0; i < 1_000_000; i++) {
String s = new String(i + "").intern();
// ОПасно: Pool переполнится
}
// Результат: OutOfMemoryError: Java heap space
2. Неопредсказуемое поведение
String s1 = "test" + "_" + System.currentTimeMillis();
// Результат всегда в Heap, никогда в Pool
String s2 = "test" + "_" + "123"; // Может оптимизироваться в "test_123" → Pool
Контроль String Pool
StringTableSize (по умолчанию 60013)
# Java опция для изменения размера Pool
java -XX:StringTableSize=100000 MyApp
# Слишком большой размер → больше памяти
# Слишком маленький размер → медленнее выполняется
Лучшие практики
// 1. Не используй intern() без необходимости
String s = userInput.intern(); // ❌ Опасно
// 2. Используй intern() только для известных значений
String country = getUserCountry().intern(); // Может быть OK если ограниченное количество
// 3. Сравни строки через equals(), не ==
if (s1.equals(s2)) { } // ✓ Правильно
if (s1 == s2) { } // ❌ Неправильно (зависит от памяти)
// 4. Используй StringBuilder для конкатенации в цикле
StringBuilder sb = new StringBuilder();
for (String s : strings) {
sb.append(s); // Эффективнее чем s1 + s2 + s3 + ...
}
// 5. Будь осторожен с кэшированием через intern()
Map<String, String> cache = new HashMap<>();
cache.put(s1.intern(), value); // Может привести к утечке памяти
Вывод
- Литеральные строки ("Hello") → String Pool (в Heap)
- new String(...) → обычный Heap
- Результаты методов (substring, toUpperCase и т.д.) → обычный Heap
- intern() → добавляет в String Pool
- == для строк опасно → используй equals()
- String Pool помогает экономить память для повторяющихся строк
- Java 7+: String Pool в Heap, управляется GC