Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Как JVM использует память
JVM управляет памятью программы через структурированную архитектуру, которая делит всю доступную память на несколько областей. Это позволяет оптимизировать производительность и обеспечивать безопасность.
Структура памяти JVM
┌──────────────────────────────────────────────────┐
│ RUNTIME DATA AREAS │
├──────────────────────────────────────────────────┤
│ HEAP (Куча) │
│ ├─ Young Generation │
│ │ ├─ Eden Space │
│ │ └─ Survivor Spaces (S0, S1) │
│ └─ Old Generation │
│ (Управляется Garbage Collector) │
├──────────────────────────────────────────────────┤
│ STACK (Стек) — по одному на каждый поток │
│ (Автоматически освобождается при выходе) │
├──────────────────────────────────────────────────┤
│ METHOD AREA │
│ (Структура класса, методы, constant pool) │
├──────────────────────────────────────────────────┤
│ PROGRAM COUNTER REGISTER │
│ (Текущая выполняемая инструкция) │
├──────────────────────────────────────────────────┤
│ NATIVE METHOD STACK │
│ (Код на C/C++ из native методов) │
└──────────────────────────────────────────────────┘
1. HEAP (Куча) — основное хранилище объектов
Это область памяти, где создаются все объекты Java. Она общая для всех потоков и управляется Garbage Collector.
public class HeapExample {
public static void main(String[] args) {
// Эти объекты создаются в HEAP
String name = "Alice"; // Строка в Heap
Person person = new Person(); // Объект Person в Heap
List<String> list = new ArrayList<>(); // ArrayList в Heap
// Ссылка на объект (переменная) хранится в STACK
// но сам объект находится в HEAP
}
}
class Person {
String name; // Поле в объекте (в Heap)
int age; // Примитив в объекте (в Heap)
}
Heap разделён на поколения:
HEAP
┌──────────────────────────────────┐
│ Young Generation (молодое) │
│ ┌────────────────────────────┐ │
│ │ Eden Space (80%) │ │
│ │ [=====объекты=====] │ │
│ └────────────────────────────┘ │
│ ┌─────────────┐ ┌──────────────┐│
│ │ Survivor 0 │ │ Survivor 1 ││
│ │ (10%) │ │ (10%) ││
│ └─────────────┘ └──────────────┘│
├──────────────────────────────────┤
│ Old Generation (старое) │
│ ┌────────────────────────────┐ │
│ │ [объекты с долгой жизнью] │ │
│ │ (выжившие несколько Minor │ │
│ │ GC или большие объекты) │ │
│ └────────────────────────────┘ │
└──────────────────────────────────┘
2. STACK (Стек) — локальные переменные
Каждый поток имеет свой стек. Здесь хранятся:
- Локальные переменные (примитивы и ссылки)
- Ссылки на объекты (сами объекты в Heap)
- Information о вызовах методов
public class StackExample {
public static void method1() {
int x = 5; // ← Примитив x в STACK
String str = "hello"; // ← Ссылка str в STACK
// ← Объект String в HEAP
method2(x);
}
public static void method2(int param) {
int y = 10; // ← Примитив y в STACK
// Когда метод завершится, y и param автоматически удалятся
}
}
Stack очищается автоматически:
Вызов method1():
┌─────────────────┐
│ x = 5 │ ← локальная переменная
│ str = [REF] │ ← ссылка на объект в HEAP
│ (method1) │
└─────────────────┘
Вызов method2(5) из method1:
┌─────────────────┐
│ param = 5 │ ← параметр метода
│ y = 10 │ ← локальная переменная
│ (method2) │
├─────────────────┤
│ x = 5 │
│ str = [REF] │
│ (method1) │
└─────────────────┘
Выход из method2():
┌─────────────────┐
│ x = 5 │ ← param и y удалились
│ str = [REF] │
│ (method1) │
└─────────────────┘
3. Примеры использования памяти
public class MemoryExample {
public static void main(String[] args) {
// STACK: переменная main
Person person = createPerson();
// STACK: person (ссылка)
// HEAP: объект Person
printPerson(person);
}
public static Person createPerson() {
// STACK: person (локальная переменная метода)
Person person = new Person("Alice", 30);
// HEAP: создан объект Person с полями
return person;
// STACK: person удалится, но объект останется в HEAP
// (на него есть ссылка в main)
}
public static void printPerson(Person p) {
// STACK: p (параметр метода, ссылка)
// p указывает на тот же объект в HEAP
System.out.println(p.getName());
// STACK: p удалится при выходе из метода
}
}
class Person {
private String name; // HEAP
private int age; // HEAP (примитив)
public Person(String name, int age) {
// STACK: name, age (параметры)
// this.name, this.age указывают на HEAP
this.name = name;
this.age = age;
}
public String getName() {
return this.name; // возвращаем ссылку на HEAP
}
}
4. Garbage Collection
GC автоматически удаляет объекты, на которые нет ссылок:
public class GCExample {
public static void main(String[] args) {
Person p1 = new Person("Alice");
// HEAP: объект Person (на него ссылка p1)
Person p2 = p1;
// Тот же объект, две ссылки
p1 = null;
// HEAP: объект всё ещё жив (на него ещё ссылка p2)
p2 = null;
// HEAP: объект теперь unreachable
// → GC удалит этот объект на следующем цикле
}
}
5. Generational Garbage Collection
Молодые объекты чаще удаляются, старые реже:
Объект создан → Eden Space
↓
Minor GC
↓
Survivor Space 0
↓
Minor GC
↓
Survivor Space 1
↓
Minor GC (несколько раз)
↓
Old Generation
↓
Major GC (редко)
public class GenerationalGC {
public static void main(String[] args) {
// Короткоживущие объекты
for (int i = 0; i < 1_000_000; i++) {
String temp = "temporary_" + i;
// Объект в Eden Space
// Удалится на первом Minor GC
}
// Долгоживущий объект
Person persistent = new Person("Alice");
// Объект в Eden Space
// Выживет несколько Minor GC
// Переместится в Old Generation
// долгая работа...
doSomething(persistent);
}
}
6. Конфигурация памяти JVM
# Размеры памяти
java -Xms1024m -Xmx4096m MyApp
# -Xms: начальный размер Heap = 1 GB
# -Xmx: максимальный размер Heap = 4 GB
# Stack size (на поток)
java -Xss256k MyApp
# Каждый поток получает 256 KB stack
# PermGen / Metaspace (для класса данных)
java -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=256m MyApp
public class MemoryInfo {
public static void main(String[] args) {
Runtime runtime = Runtime.getRuntime();
long totalMemory = runtime.totalMemory();
long freeMemory = runtime.freeMemory();
long usedMemory = totalMemory - freeMemory;
long maxMemory = runtime.maxMemory();
System.out.println("Total: " + totalMemory / (1024 * 1024) + " MB");
System.out.println("Used: " + usedMemory / (1024 * 1024) + " MB");
System.out.println("Free: " + freeMemory / (1024 * 1024) + " MB");
System.out.println("Max: " + maxMemory / (1024 * 1024) + " MB");
}
}
7. OutOfMemoryError
// ❌ HEAP overflow
List<byte[]> list = new ArrayList<>();
while (true) {
list.add(new byte[1024 * 1024]); // 1 MB
// В итоге: Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
}
// ❌ StackOverflow
public static void recursion() {
recursion(); // Бесконечная рекурсия
// Exception in thread "main" java.lang.StackOverflowError
}
// ❌ Metaspace overflow (много классов)
// Обычно только в очень специфичных ситуациях (динамическое создание классов)
8. Лучшие практики управления памятью
// ✅ Правильно: явно освобождаем ресурсы
try (FileReader reader = new FileReader("file.txt")) {
// Ресурс автоматически закроется
} catch (IOException e) {
e.printStackTrace();
}
// ✅ Правильно: не держим ненужные ссылки
public void processData() {
byte[] largeData = loadLargeFile();
// обрабатываем
largeData = null; // явно освобождаем для GC
}
// ❌ Неправильно: утечка памяти
static List<Object> cache = new ArrayList<>();
public void add(Object obj) {
cache.add(obj); // Объекты никогда не удалятся!
}
// ✅ Правильно: WeakReference для кэша
static Map<Object, WeakReference<Object>> cache = new WeakHashMap<>();
Заключение
JVM использует многоуровневую архитектуру памяти:
- Heap — объекты, управляется GC
- Stack — локальные переменные, очищается автоматически
- Method Area — информация о классах
- GC — автоматически удаляет неиспользуемые объекты
Это позволяет разработчикам не беспокоиться о ручном управлении памятью и концентрироваться на бизнес-логике.