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


216

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

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

Ответы:


180

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

JVM может хранить данные любым удобным для себя способом, с прямым или прямым порядком байтов, с любым количеством дополнений или накладных расходов, хотя примитивы должны вести себя так, как если бы они имели официальные размеры.
Например, JVM или собственный компилятор может решить хранить 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 дополнительных байта. Использование ресурсов Integerобходится мне на 300 процентов из-за нехватки памяти по сравнению с тем, когда я могу сохранить значение как примитивный тип

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


int [128] [6]: 128 массивов по 6 дюймов - всего 768, 3072 байта данных + 2064 байта. Служебные данные объекта = всего 5166 байтов. int [256]: всего 256 дюймов - поэтому несопоставимые. int [768]: 3072 байта данных + 16 байтов служебной информации - примерно 3/5 пространства 2D-массива - не совсем 246% служебной информации!
JeeBee

Ах, оригинальная статья использовала int [128] [2], а не int [128] [6] - интересно, как это изменилось. Также показывает, что крайние примеры могут рассказать другую историю.
JeeBee

2
Служебная нагрузка составляет 16 байтов в 64-битной JVM.
Тим Купер

3
@AlexWien: Некоторые схемы сбора мусора могут устанавливать минимальный размер объекта, который отделен от отступов. Во время сборки мусора, когда объект копируется из старого местоположения в новое, старому местоположению больше не нужно хранить данные для объекта, но ему нужно будет хранить ссылку на новое местоположение; может также потребоваться сохранить ссылку на старое местоположение объекта, в котором была обнаружена первая ссылка, и смещение этой ссылки в старом объекте [поскольку старый объект все еще может содержать ссылки, которые еще не были обработаны].
суперкат

2
@AlexWien: Использование памяти в старом месте объекта для хранения бухгалтерской информации сборщика мусора избавляет от необходимости выделять другую память для этой цели, но может устанавливать минимальный размер объекта, который больше, чем требовалось бы в противном случае. Я думаю, что по крайней мере одна версия сборщика мусора .NET использует этот подход; для некоторых сборщиков мусора на Java это также возможно.
суперкат

35

Это зависит от архитектуры / 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>

28

Каждый объект имеет определенные накладные расходы для связанной с ним информации о мониторе и типе, а также самих полей. Кроме того, поля могут быть размечены в значительной степени, однако 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 байтов.

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


9
На самом деле, один экземпляр SingleByte занял бы 16 байтов в JVM Sun, то есть 8 байтов служебной информации, 4 байта для поля, а затем 4 байта для заполнения объектов, поскольку компилятор HotSpot округляет все до кратных 8.
Пол Вагланд

6

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

java.lang.Runtime.getRuntime();

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

package test;

 import java.util.ArrayList;
 import java.util.List;

 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 a object Person yourself...
         List < Person > list = new ArrayList < Person > ();
         for (int i = 0; i <= 100000; 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));
     }
 }

6

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

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

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

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

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


«Похоже, что каждый объект имеет служебную нагрузку 16 байтов в 32-битных системах (и 24 байта в 64-битных системах)». Это не правильно, по крайней мере, для нынешних JDK. Посмотрите на мой ответ для примера Integer. Накладные расходы на объект составляют не менее 12 байтов для заголовка для 64-битной системы и современного JDK. Может быть больше из-за заполнения, зависит от фактического расположения полей в объекте.
Дмитрий Спихальский

Вторая ссылка на туториал по эффективному использованию памяти кажется мертвой - я получаю «Запрещено».
Цлейсон

6

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

Нет.

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

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

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

  • Атрибуты варьируются от 1 байта (байта) до 8 байтов (long / double), но ссылки бывают либо 4 байта, либо 8 байтов, в зависимости не от того, 32-битный или 64-битный, а от того, равен -Xmx <32Gb или> = 32Gb: типичный 64 В -bit JVM есть оптимизация под названием -UseCompressedOops, которая сжимает ссылки до 4 байтов, если куча меньше 32 Гб.

1
символ 16 бит, а не 8 бит.
комонад

вы правы. кто - то , кажется, редактировал свой оригинальный ответ
Jayen

5

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


4

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

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

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

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

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

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

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

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

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

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

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

логическое значение & байт - 1

char & short - 2

int & float - 4

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


Читатели могут также найти эту статью очень полезной
подавляющее


2

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


Это отлично сработало для получения приблизительной оценки объема памяти, используемого (String, Integer)кешем Guava для каждого элемента. Спасибо!
Стив К

1

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


0

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

Для подробных правил для SUN JVM проверьте мой старый блог

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


Я вполне уверен, что Sun Java 1.6 64bit, нужно 12 байтов для простого объекта + 4 дополнения = 16; Объект + одно целое поле = 12 + 4 = 16
AlexWien

Вы закрыли свой блог?
Йохан Буле

Не совсем, Не уверен, что блоги SAP как-то изменились. Большую часть этого можно найти здесь kohlerm.blogspot.com
kohlerm
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.