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

Как JVM использует память

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

Комментарии (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 — автоматически удаляет неиспользуемые объекты

Это позволяет разработчикам не беспокоиться о ручном управлении памятью и концентрироваться на бизнес-логике.

Как JVM использует память | PrepBro