Ответ
Память JVM разделена на несколько областей (Runtime Data Areas), каждая из которых служит определенной цели в жизненном цикле программы.
Основные области памяти:
-
Heap (Куча)
- Назначение: Хранит все объекты и их поля (экземпляры классов), массивы.
- Управление: Сборщик мусора (Garbage Collector, GC) автоматически освобождает память от неиспользуемых объектов.
- Структура (поколения):
- Young Generation: Для новых объектов. Состоит из Eden и двух Survivor Spaces (S0, S1). Частый Minor GC.
- Old Generation (Tenured): Для долгоживущих объектов, переживших несколько сборок в Young Gen. Редкий, но долгий Major GC (Full GC).
-
JVM Stack (Стек)
- Назначение: Хранит фреймы методов (локальные переменные, ссылки на объекты в Heap, частичные результаты).
- Особенность: Создается для каждого потока. При переполнении возникает
StackOverflowError.
-
Metaspace (Java 8+)
- Назначение: Хранит метаданные классов (структуры классов, байт-код, константы времени компиляции).
- Важно: Заменяет PermGen (Permanent Generation). Память выделяется из Native Memory ОС, что снижает риск
OutOfMemoryError: PermGen space.
-
Native Memory (Память ОС)
- Назначение: Используется самой JVM (Metaspace, код JIT-компилятора) и нативными библиотеками (например, через
ByteBuffer.allocateDirect()).
- Назначение: Используется самой JVM (Metaspace, код JIT-компилятора) и нативными библиотеками (например, через
Визуализация и пример:
JVM Memory
├── Heap (Управляется GC)
│ ├── Young Generation
│ │ ├── Eden
│ │ └── Survivor 0/1
│ └── Old Generation
├── JVM Stack (по одному на поток)
├── Metaspace (Классы, методы)
└── Native Memory (JVM internal, NIO)
Пример утечки памяти (Memory Leak):
public class MemoryLeak {
private static final List<byte[]> LEAK = new ArrayList<>();
public static void main(String[] args) {
while (true) {
// Новый объект создается, но ссылка на него хранится в статическом списке.
// GC не может удалить эти объекты -> Heap заполняется -> OutOfMemoryError.
LEAK.add(new byte[10_000_000]); // 10 MB
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
}
}
Почему это важно? Понимание модели памяти помогает писать эффективный код, настраивать параметры JVM (-Xmx, -Xms) и диагностировать проблемы производительности.
Ответ 18+ 🔞
А, слушай, про память JVM! Ну это, блядь, целая вселенная, где каждый байт знает своё место, а если не знает — получает пизды от сборщика мусора. Сейчас разложу по полочкам, как в хорошем бардаке.
Вот представь: JVM — это такой охуенно организованный склад. И на нём есть несколько ключевых зон, куда всё складывается.
Основные закутки этого склада:
-
Heap (Куча)
- Для чего: Сюда, сука, летят все объекты, которые ты создаёшь через
new, и все массивы. Все эти твоиUser,Order,List<String>— их трупы тут и плавают. - Кто убирает: Тут работает главный дворник — Garbage Collector (GC). Он ходит и смотрит: если на объект никто не ссылается (типа, все про него забыли), он его в мусорку. Автоматом, блядь!
- Внутренний расклад:
- Young Generation (Детсад): Сюда попадают свеженькие, только что родившиеся объекты. Состоит из Eden (райский сад, где всё начинается) и двух Survivor Spaces (S0, S1, куда переезжают выжившие после первой чистки). Тут частые, но быстрые уборки — Minor GC.
- Old Generation (Геронтологическое отделение): Сюда переводятся объекты-долгожители, которые пережили несколько чисток в молодёжке. Уборки тут редкие, но, ёпта, капитальные — Major GC (или Full GC), которые могут всё заморозить.
- Для чего: Сюда, сука, летят все объекты, которые ты создаёшь через
-
JVM Stack (Стек)
- Для чего: Это, блядь, как блокнот для каждого потока. В нём хранятся фреймы методов — локальные переменные, ссылки на объекты из кучи, промежуточные результаты вычислений. Вызвал метод — новый листок в блокноте. Закончил метод — листок вырвал и выкинул.
- Подвох: Блокнот не резиновый. Если рекурсию без тормозов написать и листков станет овердохуища — получишь
StackOverflowError. Всё, пиздец, поток.
-
Metaspace (Святая святых, с Java 8+)
- Для чего: Тут живут не объекты, а метаданные классов — их структура, байт-код, константы. Раньше это была PermGen, которая постоянно дохла с ошибкой
OutOfMemoryError: PermGen space. Теперь это Metaspace — память под неё берётся прямо из Native Memory ОС, и лимиты пошире. Умнее, блядь, стали.
- Для чего: Тут живут не объекты, а метаданные классов — их структура, байт-код, константы. Раньше это была PermGen, которая постоянно дохла с ошибкой
-
Native Memory (Память ОС)
- Для чего: Это уже за пределами JVM, но она ей активно пользуется. Сама JVM тут хранит свои внутренние дела (ту же Metaspace, код от JIT-компилятора). А ещё, например, когда ты делаешь
ByteBuffer.allocateDirect()для быстрого I/O — память режется прямо тут, минуя кучу.
- Для чего: Это уже за пределами JVM, но она ей активно пользуется. Сама JVM тут хранит свои внутренние дела (ту же Metaspace, код от JIT-компилятора). А ещё, например, когда ты делаешь
Как это всё, блядь, выглядит в голове:
Память JVM
├── Куча (Heap) (Царство GC)
│ ├── Молодое поколение (Young Gen)
│ │ ├── Эдем (Eden) - тут рождается всё
│ │ └── Выжившие 0/1 (Survivor) - тут крутятся стойкие
│ └── Старое поколение (Old Gen) - тут доживают ветераны
├── Стек (JVM Stack) (По одному на поток)
├── Metaspace (Тут живут чертежи классов)
└── Native Memory (Территория ОС и нативных штук)
А вот, сука, классический пример, как всё проебать — утечка памяти (Memory Leak):
public class MemoryLeak {
private static final List<byte[]> LEAK = new ArrayList<>(); // Статический список — он живёт вечно.
public static void main(String[] args) {
while (true) {
// Каждую итерацию создаём здоровенный массив...
// И кладём ссылку на него в бессмертный статический список LEAK.
// GC смотрит: "О, на объект есть ссылка из статического поля! Удалять низя!".
// Куча медленно, но верно заполняется трупами массивов -> OutOfMemoryError.
LEAK.add(new byte[10_000_000]); // 10 МБ в петлю
try { Thread.sleep(100); } catch (InterruptedException e) {}
}
}
}
Нахуя это всё знать? Да чтобы не быть, прости господи, распиздяем! Понимая это, ты:
- Пишешь код, который не сосёт память как пылесос.
- Можешь настроить JVM флажками (
-Xmx,-Xms), чтобы твоё приложение не дохло на ровном месте. - Диагностируешь, почему всё вдруг встало колом — может, это Full GC часами ебётся, а ты и не в курсе.
Короче, память — не хуй собачий, её надо уважать.