Когда я где-то читаю, память выделяется при создании экземпляров, а пространство выделяется из кучи. Если это правильно, чем когда и сколько точно выделяется память при создании экземпляров и объектов?
Переменные, объявленные в методе, хранятся в стеке, а фактические объекты хранятся в куче. Рассматривать
Integer a = new Integer(10);
В этом примере объект типа Integer создается в куче, и ссылка (32 или 64 бит) возвращается и сохраняется в стек меток как переменная 'a'. JVM может хранить такие переменные в пределах регистров процессора, если они предпочитают оптимизацию.
Память объекта выделяется при использовании нового ключевого слова. Обычно он назначается внутри TLAB (буфера локального распределения потоков), который является частью кучи eden, зарезервированной для текущего потока. Таким образом, сокращение накладных расходов на распределение объектов до простого "ударного указателя". Два раза, когда TLAB не используется, является 1) когда объект слишком велик для оставшегося пространства, в каком случае он будет продвигаться прямо к старым поколениям и 2) когда поддерживающая JVM решает посредством анализа эвакуации, что он может избежать объект полностью и выделяется непосредственно в стек (или даже разбивать объект отдельно и назначать только поля, необходимые для стека).
Объем зарезервированной памяти состоит из заголовка объекта, обычно 2 слова (3 для массива), а затем пробела для каждого из полей, объявленных в объекте и его родительских классах. Общий размер этих полей зависит от JVM и базовой платформы (например, 32 бит или 64 бит) и конфигурации JVM, таких как сжатые ссылки.
------------------+------------------+------------------ +--------------------------
| mark word | klass pointer | array size (opt) | padding and fields |
------------------+------------------+-------------------+--------------------------
Просьба о JVM для размеров официально не поддерживается, но EHCache sizeOf - это очень хорошее "лучшее предположение", которое использует знания разных JVM и доступ к базовым указателям с использованием Java Unsafe.
Исходным местом для понимания размера каждого поля является размер примитивов, определенных языком Java, однако это только минимальные размеры, поскольку JVM предназначен для работы с 32 битами, и поэтому примитивы, меньшие, чем это, часто дополняются до 32 бит, Например, booleans.
Точная компоновка полей будет варьироваться в зависимости от JVM, но они будут сгруппированы по классу, который определяет их, начиная с корня дерева наследования. Например, рассмотрим
а также
Изображения, приведенные выше, были взяты из этого очень хорошего сообщения в блоге, которое очень хорошо описывает структуру памяти,
"create instance" имеет то же значение, что "использовать new
для создания нового объекта".
В нормальном случае память кучи будет выделена при запросе new
объекта, но это не задано в камне: HotSpot также может определить, что безопасно выделять объект в стеке вызовов (в процессе Escape Анализ). Это более эффективно, так как не требуется сбор мусора.
Сколько выделено памяти имеет высокую специфичность для реализации, только Java-массивы гарантированно будут "упакованы" (по модулю фиксированные накладные расходы). Булевы массивы, однако, указаны как занимающие байты на элемент.
Я читаю ваш вопрос как: "Кто на самом деле выделяет память для объекта - new
ключевое слово или конструктор?" Если это так, ответ будет ключевым словом new
.
Конструкторы, как правило, цепочки, что означает, что по крайней мере два конструктора будут запускаться при создании экземпляра. С другой стороны, память для экземпляра выделяется только один раз.
Кроме того, тип распределения определяется с использованием анализа использования созданной ссылки (например, анализ побега). Это означает, что наиболее очевидным местом для размещения является место вызова конструктора (то есть место new
выражения).
Размер выделенной памяти таков, что он может содержать экземпляр типа, следующего за new
ключевым словом. Последний размер (размер экземпляра типа)
int
, float
, double
и т.д.) состоит из, В любом случае, когда вы делаете T obj1 = new T()
, где T
- это имя класса:
T
obj1
. Когда вы выполняете R obj2 = new R()
, аналогичная вещь происходит для типа R
, а тип R
может иметь разный размер, чем T
Но ни одна из этих локальных переменных не содержит экземпляр. Оба они содержат ссылку на назначенный объект. (Таким образом, сами переменные могут быть даже одного размера, если все, что им нужно сделать, это сохранить адрес памяти.)
Поскольку я где-то читал, что память выделяется при создании экземпляров, а пространство выделяется из кучи.
да, вы правы, до тех пор, пока new
будет называться просто null
ссылкой, которая ничего не указывает.
Если это правильно, чем когда и насколько точно выделяется память при создании экземпляров и объектов?
Это зависит от размера объекта.
Взгляните на примитивные типы данных, чтобы узнать об их размере.
Переменные класса
Когда несколько объектов создаются из одного плана, каждый из них имеет свои собственные отдельные копии переменных экземпляра. В случае класса Bicycle переменными экземпляра являются каденция, передача и скорость. Каждый объект Bicycle имеет свои собственные значения для этих переменных, хранящихся в разных ячейках памяти.
Иногда вы хотите иметь переменные, общие для всех объектов. Это достигается с помощью статического модификатора. Поля, которые имеют статический модификатор в своем объявлении, называются статическими полями или переменными класса. Они связаны с классом, а не с каким-либо объектом. Каждый экземпляр класса использует переменную класса, которая находится в одном фиксированном месте в памяти. Любой объект может изменить значение переменной класса, но переменные класса также можно манипулировать, не создавая экземпляр класса.
https://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html