Как устроена структура памяти JVM?

Ответ

Память в JVM разделена на несколько основных областей, каждая из которых отвечает за хранение определённых данных и имеет свой жизненный цикл.

Основные области памяти JVM:

  1. Heap (Куча) — самая большая область. Здесь создаются все объекты и массивы. Управляется сборщиком мусора (GC).

    • Young Generation: Куда помещаются новые объекты. Состоит из Eden и двух Survivor Spaces. Частые минорные сборки (Minor GC).
    • Old Generation (Tenured): Сюда перемещаются объекты, пережившие несколько циклов GC в Young Generation. Редкие мажорные сборки (Major/Full GC).
  2. Metaspace (с Java 8) — заменяет устаревшую PermGen. Хранит метаданные классов, информацию о загруженных классах, статические переменные, константы пула строк.

  3. JVM Stack (Стек) — создаётся для каждого потока. Хранит фреймы методов: локальные переменные (примитивы и ссылки), частичные результаты и данные для вызовов методов.

  4. Program Counter (PC) Register — для каждого потока хранит адрес текущей выполняемой инструкции JVM.

  5. Native Method Stack — обслуживает вызовы нативных методов (написанных на C/C++).

Наглядный пример распределения:

public class MemoryStructure {
    private static int staticVar = 100; // Metaspace (раньше PermGen)
    private int instanceVar;            // Heap (в составе объекта)

    public void method() {
        int localVar = 50;              // JVM Stack (во фрейме метода 'method')
        Object obj = new Object();      // Объект в Heap, ссылка 'obj' в Stack
    }
}

Ответ 18+ 🔞

А, слушай, ну вот опять про эту память в Java. Сидишь такой, пишешь код, а оно тебе — OutOfMemoryError, пиздец! А всё потому, что не понимаешь, куда что летит. Давай разберём, как эта штука устроена, чтоб потом не охуевать.

Вот представь, что JVM — это такой большой, блядь, склад. И на этом складе есть разные отделы, куда мусор свозят по-разному. Главный отдел, самый забитый — это Heap (Куча). Туда, сука, попадает вообще всё, что ты создаёшь через new. Все твои объекты, массивы — всё там, в этой куче. А внутри этой кучи ещё деление есть, хитрая жопа!

Сначала объекты попадают в Young Generation — это типа приёмный покой для новорождённых. Там есть Eden (Эдем, иронично, да? Потому что скоро оттуда всех выгонят) и два Survivor Space. Пока объект молодой и горячий, он там скачет из Эдема в сервайворы и обратно. А как постареет, переживёт несколько уборок — его отправляют на пенсию в Old Generation. Там уже пореже мусор вывозят, но зато основательно, всей командой.

А раньше была ещё PermGen, все её боялись. Теперь её заменили на Metaspace. Это, типа, архив. Туда складывают не объекты, а чертежи к ним — метаданные классов, всякие статические переменные, названия методов. Бесконечно расширяемый архив, в теории. На практике тоже можно его дохуя раздуть, если очень постараться.

Дальше идёт JVM Stack (Стек). Это, блядь, не куча, а индивидуальная тумбочка для каждого потока. Каждый раз, когда поток заходит в метод, ему выделяют ящичек в этой тумбочке — фрейм. Туда он кладёт свои локальные переменные (примитивы и ссылки на объекты из кучи, сами понимаете). Вышел из метода — ящичек выкинули. Красиво.

Program Counter Register — это такая бумажка-напоминалка для каждого потока. На ней написано: "на каком шаге инструкций я сейчас остановился". Чтоб не потеряться.

И Native Method Stack — это отдельная комната для гостей из другого мира, для нативных методов на C++. Туда мы обычно не лезем, если не хотим проблем.

Вот смотри, как это всё в коде выглядит, чтоб не быть просто болтовнёй:

public class MemoryStructure {
    private static int staticVar = 100; // Это, блядь, летит прямиком в Metaspace. Не в кучу!
    private int instanceVar;            // А это — уже в Heap, внутри каждого созданного объекта.

    public void method() {
        int localVar = 50;              // Эта циферка живёт уютно в стеке, во фрейме метода 'method'.
        Object obj = new Object();      // Вот тут магия! Сам объект new Object() — в куче (Heap).
                                        // А ссылка 'obj' на него — в стеке, в том же фрейме.
                                        // Вышел из метода — ссылка 'obj' испарилась из стека.
                                        // А объект в куче остался, пока его сборщик мусора не приберёт.
    }
}

Вот и вся магия, ёпта. Понял, где что лежит — уже полдела в оптимизации и отладке сделал. А то будешь как Герасим — "Мууу!" говорить, когда память кончится, и нихуя не понять, почему.