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

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

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


person Community    schedule 03.11.2008    source источник


Ответы (12)


Mindprod отмечает, что на этот вопрос непросто ответить:

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

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

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

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

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

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

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

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

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

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

Упакованные типы, массивы и строки

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

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

  • Long: также 16 байт: очевидно, что фактический размер объекта в куче зависит от низкоуровневого выравнивания памяти, выполняемого конкретной реализацией JVM для определенного типа ЦП. Похоже, что Long - это 8 байтов служебных данных объекта плюс еще 8 байтов для фактического длинного значения. Напротив, 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 отслеживает рост его внутреннего массива char. Однако класс 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 байта.

person VonC    schedule 03.11.2008
comment
int [128] [6]: 128 массивов по 6 целых чисел - всего 768 целых, 3072 байта данных + 2064 байта Накладные расходы на объект = всего 5166 байтов. int [256]: всего 256 int, поэтому несопоставимо. int [768]: 3072 байта данных + 16 байтов накладных расходов - примерно 3/5 пространства 2D-массива - не совсем 246% накладных расходов! - person JeeBee; 07.04.2009
comment
Ах, в исходной статье использовалось int [128] [2], а не int [128] [6] - интересно, как это изменилось. Также показывает, что крайние примеры могут рассказать другую историю. - person JeeBee; 07.04.2009
comment
@Jeebee: я исправил опечатки. int [128] [2] стал int [128] [6] из-за ошибки в редакторе Javascript: ссылки указываются с помощью [aTest] [x], и редактор принял [128] [2] как адрес ссылки ! Он действительно переупорядочил индексы этих ссылок, заменив [2] на [6] ... сложно! - person VonC; 07.04.2009
comment
Накладные расходы составляют 16 байт в 64-битной JVM. - person Tim Cooper; 12.04.2013
comment
@TimCooper Нет! Накладные расходы (= new Object ()) 64-битной Sun VM Java 1.6, работающей на 64-битной Win7, Intel составляют 12 байтов + 4 отступа до следующего числа, кратного 8, составляют 16 байтов. Итак, если у вас есть объект с одним полем int, это все равно 16 байтов. (без отступов) - person AlexWien; 17.07.2014
comment
@AlexWien: Некоторые схемы сборки мусора могут устанавливать минимальный размер объекта, отдельный от заполнения. Во время сборки мусора, когда объект копируется из старого местоположения в новое, старое местоположение может больше не нуждаться в хранении данных для объекта, но оно должно будет содержать ссылку на новое местоположение; может также потребоваться сохранить ссылку на старое местоположение объекта, в котором была обнаружена первая ссылка, и смещение этой ссылки в старом объекте [поскольку старый объект может все еще содержать ссылки, которые еще не были обработаны]. - person supercat; 19.09.2014
comment
@AlexWien: использование памяти в старом местоположении объекта для хранения бухгалтерской информации сборщика мусора позволяет избежать необходимости выделять для этой цели другую память, но может потребовать минимального размера объекта, который больше, чем требовалось бы в противном случае. Я думаю, что по крайней мере одна версия сборщика мусора .NET использует этот подход; Конечно, некоторые сборщики мусора Java тоже могли бы поступить так же. - person supercat; 19.09.2014
comment
@VonC Этот ответ устарел, потому что в наши дни многие люди используют 64-битные JVM, и ваш ответ фактически игнорирует 64-битные JVM. Хорошо сказать, что это зависит от ... но также полезно дать несколько хороших практических советов - вещи, которые верны для 99% JVM, таких как 8 байтов для 32 бит, 12 байтов + 8-байтовое выравнивание для 64 бит , плюс указатели на 8 байт выше -Xmx32G. - person Tim Cooper; 15.02.2016

Это зависит от архитектуры / 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 сжимаются на месте сразу после заголовка и перед границей заполнения.

Пример кода:

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>
person Dmitry Spikhalskiy    schedule 26.08.2015

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

public class SingleByte
{
    private byte b;
}

vs

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 байтов.

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

person Jon Skeet    schedule 03.11.2008
comment
Фактически, один экземпляр SingleByte займет 16 байтов на Sun JVM, то есть 8 байтов служебных данных, 4 байта для поля и затем 4 байта для заполнения объекта, поскольку компилятор HotSpot округляет все до кратных 8. - person Paul Wagland; 24.01.2011

Похоже, что каждый объект имеет накладные расходы в 16 байт в 32-битных системах (и 24 байта в 64-битных системах).

http://algs4.cs.princeton.edu/14analysis/ - хороший источник информации. . Среди множества хороших примеров можно привести следующий.

введите описание изображения здесь

http://www.cs.virginia.edu/kim/publicity/pldi09tutorials/memory-efficient-java-tutorial.pdf также очень информативен, например:

введите описание изображения здесь

person Arun    schedule 05.05.2015
comment
Похоже, что каждый объект имеет накладные расходы в 16 байт в 32-битных системах (и 24 байта в 64-битных системах). Это неверно, по крайней мере, для текущих JDK. Взгляните на мой ответ для целочисленного примера. Накладные расходы объекта составляют не менее 12 байт для заголовка для 64-битной системы и современного JDK. Может быть больше из-за заполнения, зависит от фактического расположения полей в объекте. - person Dmitry Spikhalskiy; 07.09.2015
comment
Вторая ссылка на учебник по Java с эффективным использованием памяти кажется мертвой - я получаю "Запрещено". - person tsleyson; 08.08.2017

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

No.

Сколько памяти отведено под объект?

  • Накладные расходы составляют 8 байтов для 32-разрядной версии, 12 байтов для 64-разрядной версии; а затем округляется до кратного 4 байтам (32-разрядная версия) или 8 байтов (64-разрядная версия).

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

  • Атрибуты варьируются от 1 байта (байта) до 8 байтов (длинный / двойной), но ссылки имеют размер 4 байта или 8 байтов в зависимости не от того, 32-битный он или 64-битный, а скорее от того, является ли -Xmx ‹32 ГБ или> = 32 ГБ: типичные 64-разрядные JVM имеют оптимизацию под названием «-UseCompressedOops», которая сжимает ссылки до 4 байтов, если размер кучи меньше 32 ГБ.
person Jayen    schedule 15.02.2016
comment
char - это 16 бит, а не 8 бит. - person comonad; 26.06.2019
comment
вы правы. кто-то, кажется, отредактировал мой исходный ответ - person Jayen; 27.06.2019

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

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));
     }
 }
person catch23    schedule 24.01.2013

Нет, для регистрации объекта тоже требуется немного памяти. 100 объектов с 1 атрибутом займут больше памяти.

person Mendelt    schedule 03.11.2008

Вопрос будет очень широким.

Это зависит от переменной класса, или вы можете вызвать использование памяти в java.

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

Память кучи, используемая объектом Java, включает

  • память для примитивных полей в соответствии с их размером (размеры примитивных типов см. ниже);

  • память для справочных полей (по 4 байта);

  • заголовок объекта, состоящий из нескольких байтов «служебной» информации;

Для объектов в java также требуется некоторая «служебная» информация, такая как запись класса объекта, идентификатора и флагов состояния, например, доступен ли объект в данный момент, заблокирован ли в данный момент синхронизация и т. Д.

Размер заголовка объекта Java варьируется на 32- и 64-битных jvm.

Хотя это основные потребители памяти, jvm также иногда требует дополнительных полей, например, для выравнивания кода и т. Д.

Размеры примитивных типов

логическое и байтовое - 1

char & short - 2

int & float - 4

длинное и двойное - 8

person Nikhil Agrawal    schedule 18.05.2013
comment
Читатели также могут найти этот документ очень поучительным: cs .virginia.edu / kim / publicity / pldi09tutorials / - person quellish; 16.01.2014

Я получил очень хорошие результаты от java .lang.instrument.Instrumentation, упомянутый в другом ответе. Хорошие примеры его использования см. В статье Счетчик памяти прибора < / em> из информационного бюллетеня специалистов по Java и библиотеки java.sizeOf на SourceForge.

person Matt Passell    schedule 04.11.2008

Если это кому-то будет полезно, вы можете загрузить с моего веб-сайта небольшой Java-агент для запроса использования памяти объект. Это также позволит вам запрашивать "глубокое" использование памяти.

person Neil Coffey    schedule 07.04.2009
comment
Это отлично сработало для получения приблизительной оценки того, сколько памяти использует (String, Integer) Guava Cache для каждого элемента. Спасибо! - person Steve K; 03.11.2015

нет, 100 маленьких объектов требует больше информации (памяти), чем один большой.

person Burkhard    schedule 03.11.2008

Правила использования памяти зависят от реализации JVM и архитектуры ЦП (например, 32-разрядная версия вместо 64-разрядной).

Подробные правила для SUN JVM см. В моем старом блог

С уважением, Маркус

person kohlerm    schedule 13.11.2008
comment
Я почти уверен, что Sun Java 1.6 64bit, нужно 12 байтов для простого объекта + 4 отступа = 16; Объект + одно целочисленное поле = 12 + 4 = 16 - person AlexWien; 16.07.2014
comment
Вы закрыли свой блог? - person Johan Boulé; 02.02.2020
comment
Не совсем. Не уверен, что блоги SAP каким-то образом переместились. Большинство из них можно найти здесь, kohlerm.blogspot.com. - person kohlerm; 08.02.2020