Вопрос-ответ

What is the memory consumption of an object in Java?

Каково потребление памяти объектом в Java?

Является ли пространство памяти, потребляемое одним объектом со 100 атрибутами, таким же, как у 100 объектов с одним атрибутом в каждом?

Сколько памяти выделяется для объекта?
Сколько дополнительного места используется при добавлении атрибута?

Переведено автоматически
Ответ 1

Mindprod указывает, что ответить на этот вопрос непросто:


JVM может хранить данные любым удобным для нее способом внутри, с большим или меньшим порядковым номером, с любым количеством дополнений или накладных расходов, хотя примитивы должны вести себя так, как если бы они имели официальные размеры.
Например, JVM или собственный компилятор могут решить сохранить a boolean[] в 64-битных фрагментах, таких как a BitSet. Вам не обязательно об этом говорить, если программа выдает те же ответы.



  • Это может привести к размещению некоторых временных объектов в стеке.

  • Это может оптимизировать некоторые переменные или полностью исключить вызовы методов, заменив их константами.

  • Это может быть версия методов или циклов, т. Е. Компилировать две версии метода, каждая из которых оптимизирована для определенной ситуации, а затем заранее решать, какую из них вызывать.


Тогда, конечно, аппаратное обеспечение и ОС имеют многослойные кэши: кэш на чипе, кэш SRAM, кэш DRAM, обычный рабочий набор оперативной памяти и резервное хранилище на диске. Ваши данные могут дублироваться на каждом уровне кэша. Вся эта сложность означает, что вы можете только очень приблизительно предсказать потребление оперативной памяти.


Методы измерения

Вы можете использовать Instrumentation.getObjectSize() для получения оценки объема памяти, потребляемой объектом.

Для визуализации фактического расположения объекта, занимаемой площади и ссылок вы можете использовать инструмент JOL (Java Object Layout).

Заголовки объектов и ссылки на объекты

В современном 64-разрядном JDK объект имеет 12-байтовый заголовок, увеличенный до размера, кратного 8 байтам, поэтому минимальный размер объекта составляет 16 байт. Для 32-разрядных JVM служебные данные составляют 8 байт, увеличенные до кратных 4 байтам. (Из ответа Дмитрия Спихальского, ответа Джейена и JavaWorld.)

Обычно ссылки имеют размер 4 байта на 32-битных платформах или на 64-битных платформах до -Xmx32G; и 8 байт выше 32 ГБ (-Xmx32G). (См. Сжатые ссылки на объекты.)

В результате 64-разрядной JVM обычно требуется на 30-50% больше места в куче. (Должен ли я использовать 32- или 64-разрядную JVM?, 2012, JDK 1.7)

Коробочные типы, массивы и строки

Упакованные оболочки имеют накладные расходы по сравнению с примитивными типами (из JavaWorld):



  • Integer: 16-байтовый результат немного хуже, чем я ожидал, потому что int значение может поместиться всего в 4 дополнительных байта. Использование an Integer обходится мне в 300% затрат памяти по сравнению с тем, когда я могу сохранить значение в виде примитивного типа


  • Long: 16 байт также: Очевидно, что фактический размер объекта в куче зависит от низкоуровневого выравнивания памяти, выполняемого конкретной реализацией JVM для определенного типа процессора. Похоже, что a Long - это 8 байт служебных данных объекта плюс еще 8 байт для фактического значения long . Напротив, Integer была неиспользуемая дыра в 4 байта, скорее всего, потому, что JVM, которую я использую, принудительно выравнивает объект по 8-байтовой границе слова.



Другие контейнеры тоже обходятся дорого:



  • Многомерные массивы: это преподносит еще один сюрприз.
    Разработчики обычно используют конструкции, подобные int[dim1][dim2] в числовых и научных вычислениях.


    В экземпляре int[dim1][dim2] массива каждый вложенный int[dim2] массив является Object самостоятельным. Каждый добавляет обычные накладные расходы на 16-байтовый массив. Когда мне не нужен треугольный или неровный массив, это представляет собой чистые накладные расходы. Влияние возрастает, когда размеры массива сильно различаются.


    Например, int[128][2] экземпляр занимает 3600 байт. По сравнению с 1040 байтами, которые использует int[256] экземпляр (который имеет ту же емкость), 3600 байт представляют собой 246-процентные накладные расходы. В крайнем случае byte[256][1] коэффициент накладных расходов равен почти 19! Сравните это с ситуацией C / C ++, в которой тот же синтаксис не увеличивает никаких накладных расходов на хранение.


  • String: рост объема памяти объекта String отслеживает рост его внутреннего массива символов. Однако класс String добавляет еще 24 байта служебных данных.


    Для непустого объекта String размером 10 символов или меньше добавленные накладные расходы по отношению к полезной полезной нагрузке (2 байта на каждый символ плюс 4 байта на длину) варьируются от 100 до 400 процентов.



Выравнивание

Рассмотрим этот пример объекта:

class X {                      // 8 bytes for reference to the class definition
int a; // 4 bytes
byte b; // 1 byte
Integer c = new Integer(); // 4 bytes for a reference
}

Наивная сумма предполагает, что экземпляр X будет использовать 17 байт. Однако из-за выравнивания (также называемого заполнением) JVM выделяет память, кратную 8 байтам, поэтому вместо 17 байт она будет выделять 24 байта.

Ответ 2

Это зависит от архитектуры процессора и JDK. Для современного JDK и 64-разрядной архитектуры объект имеет 12-байтовый заголовок и заполнение 8 байт - таким образом, минимальный размер объекта составляет 16 байт. Вы можете использовать инструмент под названием Java Object Layout, чтобы определить размер и получить подробную информацию о макете объекта любой сущности и внутренней структуре или угадать эту информацию по ссылке на класс. Пример вывода для Integer экземпляра в моей среде:

Running 64-bit HotSpot VM.
Using compressed oop with 3-bit shift.
Using compressed klass with 3-bit shift.
Objects are 8 bytes aligned.
Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

java.lang.Integer object internals:
OFFSET SIZE TYPE DESCRIPTION VALUE
0 12 (object header) N/A
12 4 int Integer.value N/A
Instance size: 16 bytes (estimated, the sample instance is not available)
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

Для Integer размер экземпляра равен 16 байтам: 4-байтовый int помещается на место сразу после 12-байтового заголовка. И ему не нужно никакого дополнительного "заполнения", потому что 16 кратно 8 (что соответствует размеру слова RAM в 64-битной архитектуре) без остатка.

Пример кода:

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.util.VMSupport;

public static void main(String[] args) {
System.out.println(VMSupport.vmDetails());
System.out.println(ClassLayout.parseClass(Integer.class).toPrintable());
}

Если вы используете maven, чтобы получить JOL:

<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.3.2</version>
</dependency>
Ответ 3

Каждый объект имеет определенные накладные расходы на связанный с ним монитор и информацию о типе, а также на сами поля. Помимо этого, поля могут быть расположены в значительной степени так, как JVM считает нужным (я полагаю), но, как показано в другом ответе, по крайней мере, некоторые JVM будут упакованы довольно плотно. Рассмотрим класс, подобный этому:

public class SingleByte
{
private byte b;
}

против

public class OneHundredBytes
{
private byte b00, b01, ..., b99;
}

В 32-разрядной JVM я бы ожидал, что 100 экземпляров SingleByte займут 1200 байт (8 байт накладных расходов + 4 байта для поля из-за заполнения / выравнивания). Я бы ожидал, что один экземпляр OneHundredBytes займет 108 байт - накладные расходы, а затем упакованные 100 байт. Это, безусловно, может варьироваться в зависимости от JVM, хотя - одна реализация может решить не упаковывать поля в OneHundredBytes, в результате чего это займет 408 байт (= 8 байт накладных расходов + 4 * 100 выровненных / дополненных байт). На 64-разрядной JVM накладные расходы также могут быть больше (не уверен).

РЕДАКТИРОВАТЬ: Смотрите Комментарий ниже; по-видимому, HotSpot расширяет границы до 8 байт вместо 32, поэтому каждый экземпляр SingleByte будет занимать 16 байт.

В любом случае, "один большой объект" будет по крайней мере столь же эффективен, как и несколько небольших объектов - для простых случаев, подобных этому.

Ответ 4

Общее количество используемой / свободной памяти программы может быть получено в программе с помощью

java.lang.Runtime.getRuntime();

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

 public class PerformanceTest {
private static final long MEGABYTE = 1024L * 1024L;

public static long bytesToMegabytes(long bytes) {
return bytes / MEGABYTE;
}

public static void main(String[] args) {
// I assume you will know how to create an object Person yourself...
List <Person> list = new ArrayList <Person> ();
for (int i = 0; i <= 100_000; i++) {
list.add(new Person("Jim", "Knopf"));
}

// Get the Java runtime
Runtime runtime = Runtime.getRuntime();

// Run the garbage collector
runtime.gc();

// Calculate the used memory
long memory = runtime.totalMemory() - runtime.freeMemory();
System.out.println("Used memory is bytes: " + memory);
System.out.println("Used memory is megabytes: " + bytesToMegabytes(memory));
}
}
java