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

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

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

Ответ 1

Mindprod указывает, что это не прямой вопрос:

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

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

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

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

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

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

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

В современном 64-битном JDK объект имеет 12-байтовый заголовок, заполненный кратным 8 байтам, поэтому минимальный размер объекта составляет 16 байтов. Для 32-разрядных JVM служебные данные составляют 8 байтов, заполненные до 4 байтов. (От Дмитрий Спихальский ответить, Jayen answer, 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 я использует выравнивание объектов силы на 8-байтовой границе слова.

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

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

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

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

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

    Для непустого String размером 10 символов или менее добавленные накладные расходы относительно полезной полезной нагрузки (2 байта для каждого char плюс 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 сжимаются на месте сразу после заголовка и перед границей заполнения.

Пример кода:

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 накладные расходы также могут быть больше (не уверены).

EDIT: см. комментарий ниже; по-видимому, HotSpot занимает до 8-байтных границ вместо 32, поэтому каждый экземпляр SingleByte занимает 16 байт.

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

Ответ 4

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

Ответ 5

Похоже, что каждый объект имеет служебную информацию 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

Ответ 6

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

Нет.

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

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

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

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

Ответ 7

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

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

Ответ 9

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

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

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

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

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

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

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

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

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

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

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

boolean и byte - 1

char и короткий - 2

int и float - 4

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

Ответ 11

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

Ответ 12

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

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

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