JavaMiddleSystem design

Как работает модель памяти Java? В чём разница между heap и stack?

Stack хранит фреймы вызовов с локальными переменными и ссылками — быстрая LIFO-структура, выделяется автоматически. Heap — общая область для всех объектов, управляемая GC, разделённая на поколения (Young/Old).

Stack: память потока

Каждый поток Java получает собственный стек. При каждом вызове метода JVM создаёт stack frame, который содержит:

  • Локальные переменные примитивных типов (int, long, boolean и др.)
  • Ссылки на объекты (сама ссылка — в стеке, объект — в куче)
  • Операндный стек для вычислений байткода
  • Ссылку на constant pool класса

По завершении метода фрейм уничтожается мгновенно — GC не участвует. Размер стека задаётся флагом -Xss (default: 512 KB–1 MB в зависимости от ОС). Переполнение → StackOverflowError.

Heap: общая куча

Все объекты и массивы размещаются в куче. Куча разделена на поколения (в G1GC — регионы):

  • Young Generation: Eden + два Survivor (S0/S1). Большинство объектов умирают здесь (Minor GC, STW, быстрый).
  • Old Generation (Tenured): объекты, пережившие порог промоций (-XX:MaxTenuringThreshold, default 15). Major/Full GC — дорогостоящий.
  • Metaspace (с Java 8): метаданные классов — вынесено из кучи в нативную память. Не ограничено по умолчанию, контролируется -XX:MaxMetaspaceSize.
// Демонстрация: что где хранится
public class MemoryDemo {
    // Метаданные класса — Metaspace
    private static final String CONSTANT = "hello";  // Ссылка — Old Gen (String pool)

    public void process() {
        int localInt = 42;            // Stack (frame текущего вызова)
        String localRef = new String("world"); // Ссылка — Stack, объект — Heap (Eden)
        Object[] array = new Object[1000];     // Массив — Heap (Eden)

        // localInt, localRef освобождаются при выходе из метода
        // array и new String(...) — кандидаты на GC
    }

    public static void main(String[] args) {
        MemoryDemo demo = new MemoryDemo(); // demo — Stack (main frame), объект — Heap
        demo.process();
        // После return из process() — объекты в Eden ждут Minor GC
    }
}

Мониторинг памяти

# Текущее потребление кучи через JMX/jcmd
jcmd <pid> VM.native_memory
jcmd <pid> GC.heap_info

# Heap dump для анализа в Eclipse MAT или VisualVM
jmap -dump:format=b,file=heap.hprof <pid>
# или флаги JVM
# -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/dumps/
// Программный доступ к метрикам памяти
import java.lang.management.*;

MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memBean.getHeapMemoryUsage();
System.out.printf("Heap: used=%d MB, max=%d MB%n",
    heapUsage.getUsed() / 1_048_576,
    heapUsage.getMax() / 1_048_576);

MemoryUsage nonHeap = memBean.getNonHeapMemoryUsage();
System.out.printf("Metaspace: used=%d MB%n",
    nonHeap.getUsed() / 1_048_576);

Подводные камни

  • Путаница «объект в стеке»: объекты всегда в куче (Escape Analysis может оптимизировать, но это детали JIT). Ссылка — в стеке или поле другого объекта.
  • String interning: строковые литералы попадают в String Pool (часть Heap с Java 7+), не создавая дублей. new String("x") — всегда новый объект в куче.
  • StackOverflowError при глубокой рекурсии: стек потока ограничен (-Xss). Альтернативы — хвостовая рекурсия (JVM не оптимизирует автоматически) или итеративный подход.
  • Metaspace OOM при динамической генерации классов: Hibernate, CGLIB, Groovy генерируют классы в рантайме — без -XX:MaxMetaspaceSize можно исчерпать нативную память.
  • Финализаторы задерживают GC: объекты с finalize() проходят дополнительный цикл — используйте Cleaner (Java 9+) или try-with-resources.
  • Неправильные -Xms/-Xmx: JVM с -Xms512m -Xmx4g постоянно расширяет кучу через GC-паузы. Для предсказуемости устанавливайте одинаковые значения.

Common mistakes

  • Путать термин «heap stack memory» с соседним механизмом Java.
  • Не называть границу lifecycle, transaction, thread или request для «heap stack memory».
  • Игнорировать production-эффекты «heap stack memory»: latency, SQL shape, memory, security или observability.

What the interviewer is testing

  • Попросить объяснить механизм «heap stack memory» на минимальном примере.
  • Проверить, видит ли кандидат failure mode и диагностику для «heap stack memory».
  • Уточнить, какие настройки или API меняют «heap stack memory» в реальном сервисе.

Sources

Related topics