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

Что такое куча?

1.2 Junior🔥 231 комментариев
#JVM и управление памятью

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

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

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

# Куча (Heap) в Java: полное объяснение

Определение

Куча (Heap) — это область памяти, которая используется для динамического выделения памяти объектам во время выполнения программы. Это главное место, где живут объекты Java.

Память в Java: Stack vs Heap

Stack (стек)

Структура: LIFO (Last In, First Out)
Примитивные типы: int, long, boolean, char, double, float
Ссылки на объекты (адреса объектов в Heap'е)
Область: на каждый поток — свой Stack
Управление: автоматическое (когда метод заканчивается — очищается)
Размер: меньше (обычно несколько МБ)

Heap (куча)

Структура: граф объектов
Объекты: new String(), new ArrayList(), user-defined классы
Область: общая для всех потоков
Управление: автоматическое (Garbage Collector)
Размер: больше (обычно несколько ГБ)

Визуализация

int age = 25;           // Stack: примитив
String name = "John";   // Stack: ссылка на объект
                        // Heap: String объект "John"
List<String> list = new ArrayList<>(); // Stack: ссылка
                        // Heap: ArrayList объект
list.add("Alice");      // Heap: String объект "Alice"
STACK (Поток 1)              HEAP (общая память)
┌─────────────────┐          ┌──────────────────┐
│ age: 25         │          │ "John" (String)  │
│ name: @0x1234 ──┼──────────→ Объект          │
│ list: @0x5678 ──┼──────────→ ArrayList объект │
│                 │          │  с "Alice"       │
└─────────────────┘          └──────────────────┘

Жизненный цикл объекта в Heap

Фаза 1: Создание (Allocation)

public class Person {
    String name;
    int age;
}

Person person = new Person(); // ШАГ 1: Выделяется память в Heap
                               // ШАГ 2: Вызывается конструктор
                               // ШАГ 3: На Stack хранится ссылка @0xABC

Фаза 2: Использование

person.name = "John";  // Изменяем значения в Heap
person.age = 30;

System.out.println(person.name); // Читаем из Heap через ссылку на Stack

Фаза 3: Удаление (Garbage Collection)

Person person = new Person(); // @0xABC в Heap
person = null;                // Ссылка удалена
// или
person = new Person();        // Ссылка теперь указывает на новый объект
// Старый объект @0xABC теперь недостижим

// Garbage Collector находит недостижимый объект и удаляет его
// Память освобождается

Управление памятью: Garbage Collection

Автоматическое управление

// В Java вы не пишете delete, как в C++

// C++:
Person* person = new Person();
delete person; // Обязательно вручную

// Java:
Person person = new Person();
person = null; // Не обязательно
// Garbage Collector сам найдёт и удалит

Когда объект становится "мусором"?

Person person = new Person(); // Объект @0xABC, ссылка на Stack
person = null;                // Ссылка удалена
// Теперь объект "мёртв" — нет пути к нему из Stack'а
// GC удалит его и освободит память

Размеры Heap'а

По умолчанию (зависит от JVM)

# На 64-bit системе
Начальный размер: 1/4 от RAM (если 8GB RAM -> 2GB Heap)
Максимальный размер: 1/4 от RAM

Установка вручную

# -Xms: начальный размер Heap'а
# -Xmx: максимальный размер Heap'а

java -Xms512m -Xmx2g MyApplication
# Начальный: 512 MB
# Максимальный: 2 GB
// В коде проверить размеры:
Runtime runtime = Runtime.getRuntime();
long maxMemory = runtime.maxMemory();        // 2 GB
long totalMemory = runtime.totalMemory();    // текущий размер
long freeMemory = runtime.freeMemory();      // свободно
long usedMemory = totalMemory - freeMemory;  // использовано

System.out.println("Max: " + maxMemory / (1024*1024) + " MB");
System.out.println("Used: " + usedMemory / (1024*1024) + " MB");

Структура Heap'а: поколения

Young Generation (молодое поколение)

Откуда: все новые объекты
Час жизни:短 (очень короткая)
ГC: частая (быстрая) сборка мусора

Структура: Eden + 2 Survivor spaces

Old Generation (старое поколение)

Откуда: долгоживущие объекты
Час жизни: долгая
ГC: редкая (медленная) сборка мусора
// Пример жизненного цикла
List<String> list = new ArrayList<>(); // Young Generation
for (int i = 0; i < 1000000; i++) {
    list.add("item" + i); // Новые String объекты в Young
}
// После некоторого времени старые объекты в Old Generation

Проблемы с Heap'ом

1. OutOfMemoryError (OOM)

// Heap переполнен, GC не может освободить память
List<String> list = new ArrayList<>();
while (true) {
    list.add(new String("data").repeat(1000)); // Бесконечно добавляем
}
// Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

2. Memory Leak

public class MemoryLeakExample {
    static List<Object> leakyList = new ArrayList<>(); // никогда не очищается
    
    public void addData() {
        for (int i = 0; i < 1000000; i++) {
            leakyList.add(new Object()); // растёт бесконечно
        }
    }
}

3. GC Pause

// Во время GC весь сервис останавливается
while (true) {
    byte[] data = new byte[10_000_000]; // создали 10 MB
    // ... работа ...
    // Когда память заканчивается -> GC останавливает все потоки
    // -> pause (может быть несколько секунд)
    // -> отключение пользователей
}

Лучшие практики

1. Не переполняйте Heap

// Плохо: создаём 1млн объектов одновременно
List<String> list = new ArrayList<>(1_000_000);
for (int i = 0; i < 1_000_000; i++) {
    list.add(new String("item" + i));
}

// Хорошо: обрабатываем потоком
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        processLine(line); // обрабатываем по одной строке
    }
}

2. Избегайте Memory Leak'ов

// Плохо
static List<String> cache = new ArrayList<>(); // растёт вечно
public void addToCache(String value) {
    cache.add(value);
}

// Хорошо
Map<String, String> cache = new LinkedHashMap<String, String>(16, 0.75f, true) {
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > 100; // максимум 100 элементов
    }
};
// или используй Guava Cache

3. Устанавливайте правильные параметры Heap'а

# Для production
java -Xms4g -Xmx4g \
     -XX:+UseG1GC \
     -XX:MaxGCPauseMillis=200 \
     MyApplication

Отладка Heap'а

Dump Heap'а для анализа

# Создать dump
jmap -dump:live,format=b,file=heap.hprof <pid>

# Анализировать в Eclipse MAT или JProfiler

JVisualVM для мониторинга

jvisualvm # GUI для просмотра использования Heap'а

Резюме

Куча (Heap) — это область памяти для хранения объектов:

  • Динамическое выделение памяти при new
  • Автоматическое удаление через Garbage Collection
  • Разделена на Young и Old Generation
  • Общая для всех потоков приложения
  • Размер ограничен параметрами JVM

Понимание Heap'а критично для:

  • Написания производительного кода
  • Избежания OutOfMemoryError
  • Профилирования приложений
  • Отладки утечек памяти
Что такое куча? | PrepBro