← Назад к вопросам
Где в памяти JVM хранятся ссылочные данные?
2.0 Middle🔥 171 комментариев
#JVM и управление памятью
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Где хранятся ссылочные данные в памяти JVM?
Это классический вопрос о структуре памяти JVM, который проверяет глубину понимания того, как работает Java под капотом. Нужно различать примитивные и ссылочные типы.
Короткий ответ
Ссылочные данные (объекты) хранятся в Heap (куче), а сами ссылки хранятся в Stack (стеке).
Stack Heap
┌─────────────────┐ ┌──────────────────────┐
│ │ │ │
│ user ──────────────→ | User object │
│ ↓ │ │ {name: "John", │
│ (ссылка) │ │ age: 30} │
│ │ │ │
└─────────────────┘ └──────────────────────┘
Детальное объяснение
Примитивные типы
int age = 25;
boolean isActive = true;
double salary = 5000.0;
// Хранятся в Stack:
Stack:
┌─────────────────┐
│ age = 25 │
│ isActive = true │
│ salary = 5000.0 │
└─────────────────┘
Ссылочные типы
String name = "John";
User user = new User("Jane", 30);
List<Integer> numbers = new ArrayList<>();
// Ссылки хранятся в Stack, объекты в Heap:
Stack Heap
┌──────────────────┐ ┌─────────────────────┐
│ name ────────────────→ │ "John" (String) │
│ │ │ │
│ user ────────────────→ │ User {name: "Jane", │
│ │ │ age: 30} │
│ │ │ │
│ numbers ─────────────→ │ ArrayList [] │
│ │ └─────────────────────┘
└──────────────────┘
Разные области памяти JVM
1. Stack (Стек)
Характеристики:
- Хранит ссылки и примитивные переменные
- Организован как LIFO (Last In, First Out)
- Один stack на каждый поток (thread-safe)
- Память автоматически освобождается при выходе из области видимости
- Размер ограничен (обычно -Xss1024k)
- Быстрая работа
public void example() {
int x = 10; // Stack: x = 10
String s = "hello"; // Stack: s → (ссылка), Heap: "hello"
{ // Вложенный блок
int y = 20; // Stack: y = 20
} // y удалена из stack
// x все еще в stack
} // x и s удалены при выходе из метода
2. Heap (Куча)
Характеристики:
- Хранит все объекты (String, User, ArrayList и т.д.)
- Общая для всех потоков
- Управляется Garbage Collector
- Размер ограничен (обычно -Xmx1024m)
- Медленнее чем Stack
- Может вызвать OutOfMemoryError
User user1 = new User("John"); // Heap: User object
User user2 = new User("Jane"); // Heap: другой User object
User user3 = user1; // Stack: user3 → тот же объект что user1
// Heap содержит 2 объекта User
// Stack содержит 3 ссылки (user1, user2, user3)
3. String Pool (специальная область в Heap)
// String Literal Pool в Heap
String s1 = "hello"; // Создается один раз в pool
String s2 = "hello"; // Ссылается на тот же объект в pool
String s3 = new String("hello"); // Новый объект в обычной heap
s1 == s2; // true (одна и та же ссылка)
s1 == s3; // false (разные объекты)
s1.equals(s3); // true (одинаковое содержимое)
4. PermGen / Metaspace (Java 8+)
До Java 8: PermGen
Хранит:
- Метаданные класса
- Константы
- Code кэш
Java 8+: Metaspace
Хранит:
- Метаданные класса
- Методы
- Информация о типах
public class MyClass {
public static final String CONSTANT = "value";
// Метаданные MyClass, CONSTANT → Metaspace
// String value → Heap
}
Визуальная схема памяти JVM
┌─────────────────────────────────────────────────────────┐
│ JVM Memory │
├─────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Stack (Thread 1) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ local variables, ссылки на объекты │ │
│ │ size: ограничено (-Xss) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Stack (Thread 2) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ (другой stack для другого потока) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Heap │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ Young Generation (Eden, S0, S1) │ │
│ │ Old Generation │ │
│ │ Размер: ограничено (-Xmx) │ │
│ │ GC управляет этой областью │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Metaspace (Java 8+) │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ Metadata классов, методы, информация о типах │ │
│ │ Может использовать Native Memory │ │
│ └──────────────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ Code Cache │ │
│ ├──────────────────────────────────────────────────┤ │
│ │ Скомпилированный код (JIT компилятор) │ │
│ └──────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────┘
Практический пример
public class MemoryExample {
public static void main(String[] args) {
// 1. Stack
int x = 10; // Stack: x = 10 (примитив)
// 2. Heap + Stack
String name = "Alice"; // Stack: name (ссылка)
// Heap: "Alice" (String)
// 3. Heap + Stack
User user = new User("Bob", 25); // Stack: user (ссылка)
// Heap: User объект
// 4. Heap + Stack
List<Integer> numbers = new ArrayList<>();
numbers.add(1); // Stack: numbers (ссылка)
// Heap: ArrayList объект
processData(x, user, numbers);
}
public static void processData(int num, User user, List<Integer> list) {
// Stack (новый frame для этого метода):
// - num = 10
// - user → (ссылка на объект в Heap)
// - list → (ссылка на ArrayList в Heap)
} // Stack frame удален при выходе из метода
}
// Где каждая переменная:
// x → Stack
// name → Stack (ссылка), "Alice" → String Pool (часть Heap)
// user → Stack (ссылка), User{} → Heap
// numbers → Stack (ссылка), ArrayList → Heap
OutOfMemoryError: два типа
1. java.lang.OutOfMemoryError: Java heap space
// ❌ Heap переполнен объектами
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1024 * 1024]); // Добавляем 1MB
}
// Ошибка: OutOfMemoryError: Java heap space
Решение: увеличить heap
java -Xmx1024m MyApp
2. java.lang.OutOfMemoryError: PermGen space (Java 7)
// ❌ PermGen переполнен метаданными класса
while (true) {
URL classUrl = new URL("file://ClassFile.class");
URLClassLoader loader = new URLClassLoader(new URL[]{classUrl});
loader.loadClass("ClassFile");
// Много классов загружено, PermGen переполнен
}
Решение (Java 7):
java -XX:PermSize=128m -XX:MaxPermSize=256m MyApp
Решение (Java 8+): Metaspace использует native memory, обычно проблем нет
StackOverflowError
// ❌ Stack переполнен рекурсией
public static void infiniteRecursion() {
infiniteRecursion(); // Бесконечная рекурсия
}
// Ошибка: java.lang.StackOverflowError
// Каждый вызов добавляет frame в stack
// ✅ Правильно
public static int factorial(int n) {
if (n <= 1) return 1; // Base case
return n * factorial(n - 1);
}
Best Practices
1. ✅ Примитивные типы → Stack
2. ✅ Ссылки → Stack, объекты → Heap
3. ✅ Помни о String Pool при работе со строками
4. ✅ Явно обнуляй большие ссылки если больше не нужны
5. ✅ Избегай утечек памяти (unclosed resources)
6. ✅ Используй try-with-resources для автоматического закрытия
7. ❌ НЕ создавай бесконечные рекурсии (StackOverflowError)
8. ❌ НЕ храни огромные коллекции без надобности
// Пример: try-with-resources (автоматически закрывает ресурс)
try (FileInputStream fis = new FileInputStream("file.txt")) {
// Работа с файлом
} // Файл автоматически закрыт
// Против: ручное управление
FileInputStream fis = new FileInputStream("file.txt");
try {
// Работа
} finally {
fis.close(); // Не забыть закрыть!
}
Команды мониторинга памяти
# Просмотр параметров JVM
jps -l # Список Java процессов
# Детальная информация о памяти
jstat -gc <pid> 1000 # Вывод GC статистики каждую секунду
# Heap dump
jmap -dump:live,format=b,file=heap.bin <pid>
# Анализ heap dump
jhat heap.bin # Анализ дампа памяти