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» в реальном сервисе.