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

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

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

12 ответов

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

JVM может хранить данные любым удобным для себя способом, с прямым или прямым порядком байтов, с любым количеством дополнений или накладных расходов, хотя примитивы должны вести себя так, как если бы они имели официальные размеры.
Например, JVM или собственный компилятор может решить сохранить boolean[] в 64-битных длинных кусках, как 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 для конкретного типа процессора. Это выглядит как Long составляет 8 байтов служебной информации объекта плюс еще 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 Рост памяти отслеживает рост внутреннего массива символов. Тем не менее 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 байта.

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

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

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

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

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

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

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

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));
     }
 }

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

Нет.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

char & short - 2

int & float - 4

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

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

Я получил очень хорошие результаты от подхода http://java.sun.com/javase/6/docs/api/java/lang/instrument/Instrumentation.html, упомянутого в другом ответе. Хорошие примеры его использования см. В статье " Счетчик памяти инструментария из информационного бюллетеня JavaSpecialists" и в библиотеке java.sizeOf на SourceForge.

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

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

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

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

Другие вопросы по тегам