Управление памятью стека и кучи Java

Я хочу знать, как выделяется память в следующей программе:

public class MemoryClass {

    public static void main(final String[] args) {
        int i = 0;
        MemoryClass memoryClass = new MemoryClass();
        memoryClass.myMethod(memoryClass);
    }

    private void myMethod(final Object obj) {
        int i = 1;
        String s = "HelloWorld!";

    }

}

теперь, насколько я понимаю, следующая диаграмма описывает, как происходит распределение памяти:
Basic runtime memory allocation


На приведенной выше диаграмме ,obj и s, которые находятся в памяти стека, на самом деле являются ссылками на их "реальных объектов", расположенных внутри памяти.
Вот набор вопросов, которые приходят мне на ум:

  1. где методы s хранить?
  2. создал ли я другой объект MemoryClass внутри myMethod, будет ли JVM выделять память для тех же методов снова внутри памяти стека?
  3. освободит ли JVM память, выделенную myMethod Как только его выполнение будет завершено, если да, то как он будет управлять ситуацией, упомянутой в вопросе 2(применимо, только если JVM выделяет память несколько раз одному и тому же методу).
  4. s и не инициализировал его, будет ли JVM по-прежнему выделять память всем методам java.lang.String класс,если да,то почему?

3 ответов


где хранятся методы s?

они хранятся в объекте класса String; это объект, загруженный объектом ClassLoader, когда строка впервые упоминается в программе. Все реализации JVM, которые существовали, когда я читал об этом последнем, никогда не освобождали память для объекта класса после его загрузки. Он на куче.

если бы я создал другой объект MemoryClass внутри myMethod, JVM выделил бы память для тех же методов снова внутри памяти стека?

нет, методы и данные для объектов хранятся отдельно, в частности, потому, что JVM никогда не требуется более одной копии методов.

освободит ли JVM память, выделенную myMethod, как только она будет выполнена, если да, то как она будет управлять ситуацией, упомянутой в вопросе 2(применимо только в том случае, если JVM выделяет память несколько раз тому же методу).

нет. Ява обычно не "сразу освобождает память" от вещей, хранящихся в куче. Он будет работать слишком медленно. Он освобождает память только тогда, когда работает сборщик мусора, и делает это только тогда, когда его алгоритм запуска сборщика мусора решает, что пришло время.

что было бы, если бы я только объявил s и не инициализировал его, JVM все равно выделил бы память всем методам java.ленг.Класс String,если да,то почему?

Это зависит по реализации JVM, я думаю, и, возможно, компилятор. Если вы объявляете переменную и никогда не используете ее, компилятор вполне может (и часто) заметить, что она бесполезна, и не помещать ее в файл класса. Если его нет в файле класса, на него никогда не ссылаются, и поэтому он и его методы не загружаются и т. д. Если компилятор помещает его в любом случае, но на него никогда не ссылаются, тогда у загрузчика классов не было бы причин загружать его, но я немного расплывчат ли это напьется или нет. Может зависеть от реализации JVM; загружает ли он вещи, потому что есть переменные класса или только когда на них ссылаются? Сколько алгоритмов ClassLoader может танцевать на головке 4-значного PIN-кода?


первым делом: Я предполагаю, что ваши вопросы выходят после прочтения этой статьи (потому что там я вижу очень похожую диаграмму, как ваша) поэтому я не буду цитировать или выделять какие-либо моменты, которые упоминаются там, и попытаюсь ответить на ваши вопросы с точками, которые не были столь очевидны в этом посте.

чтение ко всем вашим вопросам, мое впечатление, что вы ясно о том, как память выделяется в стеке и куче, но есть сомнения относительно метаданных классов, т. е. где в памяти будут храниться методы классов и как они будут переработаны. Итак, сначала позвольте мне попытаться объяснить области памяти JVM:


области памяти JVM

позвольте мне начать с размещения этих 2 диаграмм, изображающих области памяти JVM:

источник схемы

enter image description here

источник из схемы

enter image description here

теперь, как ясно из приведенных выше диаграмм ниже, это древовидная структура памяти JVM, и я попытаюсь пролить свет на то же самое (@Adit: обратите внимание, что область, которая вас касается, - это пространство PermGen или постоянное пространство генерации памяти без кучи).

  • кучу
    • молодое поколение
      • Eden Space
      • Выживший Космос!--54-->
    • старое поколение
      • Поколение Tenured
  • остальной
    • Постоянный
    • Кэш Код (Я думаю, включен "только" HotSpot Java VM)

кучу

память кучи-это область данных среды выполнения, из которой Java VM выделяет память для всего класса экземпляры и массивы. Кучи может иметь фиксированный или переменный размер. Сборщик мусора-это автоматическая система управления памятью, восстанавливает память "кучи" для объектов.

молодые

молодое поколение-это место, где создаются все новые объекты. Когда молодое поколение заполняется, производится сбор мусора. Эта сборка мусора называется Minor GC. Молодое поколение делится на 2 части ниже

Eden space: Пул, из которого первоначально выделяется память для большинства объектов.

Survivor space: пул, содержащий объекты, которые пережили сбор мусора пространства Эдема.

старый

память старого поколения содержит объекты, которые долго жили и выжили после многих раундов незначительных GC. Обычно сбор мусора выполняется в памяти старого поколения, когда она заполнена. Сбор мусора старого поколения называется Major GC и обычно занимает больше времени. Старое поколение содержит ниже Часть:

хранилища: пул, содержащий объекты, которые существовали в течение некоторого времени в оставшемся в живых пространстве.

не "кучи"

память без кучи включает область метода, разделяемую между всеми потоками и памятью, необходимой для внутренней обработки или оптимизации для Java VM. Он хранит структуры для каждого класса, такие как постоянный пул времени выполнения, данные поля и метода, а также код для методов и конструкторов. Область метода логически является частью кучи, но, в зависимости от реализации, Java VM не может собирать мусор или компактировать его. Как в динамической памяти, область метода может иметь фиксированный или переменный размер. Память для области метода не должна быть непрерывной.

постоянный

пул, содержащий все отражающие данные самой виртуальной машины, такие как объекты класса и метода. С Java VMs, которые используют класс обмена данными, это поколение делится на области только для чтения и чтения-записи.

кэш код

HotSpot Java VM также включает кэш кода, содержащий память, которая используется для компиляции и хранения собственного кода.


отвечая на вопросы OP конкретно

где хранятся методы s?

память без кучи -- > постоянное поколение

Я создал еще один объект MemoryClass внутри myMethod, будет JVM выделить память для тех же методов снова внутри памяти стека?

память стека содержит только локальные переменные, поэтому ваш ORV (объектная ссылочная переменная) new MemoryClass все равно будет создан в кадре стека myMethod, но JVM не будет загружать все методы, метаданные и т. д. из MemoryClass снова в "постоянном поколении".

JVM загружает класс только один раз, и когда он загружает класс, то пространство выделено на "постоянное поколение" для этого класса, и это происходит только один раз, когда класс загружается JVM.

освободит ли JVM память, выделенную myMethod, как только она выполнение завершено, если да, то как он будет управлять ситуацией упомянуто в вопросе 2 (применимо только в том случае, если JVM выделяет память несколько раз к тому же методу).

рамка стека создана для myMethod будет удален из памяти стека, поэтому вся память созданный для локальных переменных будет очищен, но это не означает, что JVM очистит память, выделенную в "постоянном поколении" для класса, который вы создали в myMethod

что было бы, если бы я только объявил s и не сделал инициализируйте его, будет ли JVM по-прежнему выделять память всем методам Ява.ленг.Класс String, если да, то почему?

в частности речь идет о String класс, JVM бы выделил место для String в" постоянном поколении "слишком рано, в то время как JVM запускается и инициализируете ли вы свою строковую переменную или нет, это не имеет значения с точки зрения" постоянного поколения".

говоря о других пользовательских классах, JVM загрузит класс и выделит память в "постоянном поколении", как только вы определите класс, опять же, даже если вы не создаете объект класса, память выделяется в" постоянном поколении" (не "кучи" площадью) и когда вы создаете объект класса, то память выделяется в "Eden Space" (куча площадью).


источники вышеуказанной информации и далее чтение:


поскольку принятый ответ Арси и ответ хагравала ясны, просто хочу уточнить четвертый вопрос:

Что было бы, если бы я только объявил s и не сделал инициализируйте его, будет ли JVM по-прежнему выделять память всем методам Ява.ленг.Класс String,если да,то почему?

в основном, пока верно, что данные класса, которые имеют информацию о полях и методах, хранятся в постоянном поколении (мета-пространстве начиная с JDK-8), важно отметить, что его объекты внутри java.ленг.Класс String (например, char [], который содержит всю символьную информацию для этой строки), для которого данные выделяются в куче.

Это не произойдет, пока не будет создан новый строковый объект - либо с помощью ключевого слова "new", либо путем создания нового строкового литерала (например:"helloworld").