Какое потребление памяти у объекта в Java? - PullRequest
201 голосов
/ 03 ноября 2008

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

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

Ответы [ 12 ]

168 голосов
/ 03 ноября 2008

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 .)

Как правило, ссылки являются 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 байта.

29 голосов
/ 26 августа 2015

Это зависит от архитектуры / 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>
27 голосов
/ 03 ноября 2008

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

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

5 голосов
/ 15 февраля 2016

Является ли пространство памяти, потребляемое одним объектом со 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 байтов, если куча меньше 32Gb.
5 голосов
/ 06 мая 2015

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

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

enter image description here

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

enter image description here

5 голосов
/ 03 ноября 2008

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

4 голосов
/ 24 января 2013

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

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));
     }
 }
3 голосов
/ 18 мая 2013

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

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

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

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

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

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

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

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

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

Хотя это основные потребители памяти, для jvm также требуются дополнительные поля, например, для выравнивания кода e.t.c.

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

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

char & short - 2

int & float - 4

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

3 голосов
/ 04 ноября 2008

Я получил очень хорошие результаты от подхода java.lang.instrument.Instrument , упомянутого в другом ответе. Хорошие примеры его использования см. В записи Счетчик памяти инструментария в бюллетене JavaSpecialists 'Newsletter и библиотеке java.sizeOf в SourceForge.

2 голосов
/ 07 апреля 2009

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

...