Как рассчитать размер памяти массива Java?

Я знаю, как рассчитать размер памяти объекта Java, добавив три части: заголовок + свойства + ссылка.

Я также знаю, что массив Java тоже является объектом.

Но когда я прочитал «Общие сведения о расширенных функциях и передовых методах JVM, второе издание», в нем говорится, что заголовок массива Java состоит из трех частей; отметьте слово, указатель класса и длину массива.

В 64-битной JVM Hotspot это всегда будет 24 байта.

Но как я могу рассчитать размер памяти массива Java в 32-битной JVM?

Надеюсь, вы, ребята, дадите мне пример кода Java, чтобы показать мне, как вычислить размер памяти объекта, не ограниченный объектом массива.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
0
925
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете проверить это с помощью Фреймворк JOL, написанного всесильным Алексеем Шипилевым.

Использовать его на самом деле довольно просто, сначала давайте определим макеты, которые вам нужны:

Layouter layout32Bits =  new HotSpotLayouter(new X86_32_DataModel());
Layouter layout64Bits = new HotSpotLayouter(new X86_64_DataModel());
Layouter layout64BitsComp = new HotSpotLayouter(new X86_64_COOPS_DataModel());

А затем давайте определим массив и посмотрим на результаты:

int [] ints = new int[1];
System.out.println(ClassLayout.parseInstance(ints, layout32Bits).toPrintable());
System.out.println(ClassLayout.parseInstance(ints, layout64Bits).toPrintable());
System.out.println(ClassLayout.parseInstance(ints, layout64BitsComp).toPrintable());

Давайте бегать по очереди. Для 32bits VM:

  [I object internals:
  OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
  0     4        (object header)                           09 00 00 00 (00001001 00000000 00000000 00000000) (9)
  4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
  8     4        (object header)                           10 0b 40 29 (00010000 00001011 01000000 00101001) (692062992)
 12    40    int [I.<elements>                             N/A
 52    12        (loss due to the next object alignment)
 Instance size: 64 bytes
 Space losses: 0 bytes internal + 12 bytes external = 12 bytes total

Таким образом, вы получаете 12 bytes для заголовков (4 + 4 для двух заголовков, плюс 4 для размера массива, это int); тогда вы получите 40 байта для 10 целых чисел, которые будет содержать массив.

Затем я не совсем уверен, что понимаю. Пока у нас есть байты 52, и объекты выровнены на 8 bytes, то есть это значение 52 следует округлить до 56 bytes, чтобы выровнять его до 8.

Вместо этого написано 12 bytes loss due to the next object alignment. Я могу только догадываться, что потенциально может быть две вещи: сначала прочтите комментарии здесь или может быть какое-то поле предназначено только для внутреннего использования.

Я не собираюсь показывать здесь остальные примеры вывода (вы тоже можете это сделать), и я задам дополнительный вопрос о том, что мне не ясно о заполнении в ближайшее время.

Ответ принят как подходящий

Фактический размер объекта зависит от реализации, и нет даже требования, чтобы необходимый размер для объекта оставался неизменным в течение его жизненного цикла.

статья на wiki.openjdk.java.net гласит:

Object header layout

An object header consists of a native-sized mark word, a klass word, a 32-bit length word (if the object is an array), a 32-bit gap (if required by alignment rules), and then zero or more instance fields, array elements, or metadata fields. (Interesting Trivia: Klass metaobjects contain a C++ vtable immediately after the klass word.)

The gap field, if it exists, is often available to store instance fields.

If UseCompressedOops is false (and always on ILP32 systems), the mark and klass are both native machine words. For arrays, the gap is always present on LP64 systems, and only on arrays with 64-bit elements on ILP32 systems.

If UseCompressedOops is true, the klass is 32 bits. Non-arrays have a gap field immediately after the klass, while arrays store the length field immediately after the klass.

Вы неправильно рассчитываете «заголовок + свойства + ссылка» для размера объекта. Во-первых, ссылки на объект не являются частью размера объекта референта. Может быть произвольное количество ссылок на один и тот же объект, но эти ссылки не обязательно должны находиться в памяти кучи или в ОЗУ, поскольку оптимизированный код может обращаться к объекту исключительно через регистры ЦП.

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

Для массивов из процитированной статьи вы можете вывести, что в 32-битном представлении HotSpot используется заголовок из 12 байтов, если типом не является long[] или double[], в этом случае это будет 16 байтов.

Для 64-битной реализации опция UseCompressedOops (которая включена по умолчанию) позволяет объединить 64-битное слово-метку с 32-битным классом и 32-битной длиной в заголовок общим размером 16 байт. Только если UseCompressedOops выключен, заголовок будет 24 байта.

насчет наследования ты абсолютно прав ... bugs.openjdk.java.net/browse/JDK-8024913; вы почти правы насчет 16 bytes на long / double - на самом деле это все еще 12 bytes, но long и double являются «особенными» и не могут взять эти 4 байта, оставшиеся от заголовков ..., так что дополнительные 4 байта на самом деле являются заполнением.

Eugene 24.05.2018 16:59

@Eugene Я сослался на формулировку цитаты, в которой «поле пробелов» рассматривается как часть заголовка в первом абзаце. Конечно, это немного странно, учитывая, что следующее предложение «Поле пробела, если оно существует, часто доступно для хранения полей экземпляра», если понимать это буквально, подразумевает, что в заголовке объекта могут быть поля экземпляра. Я думаю, что лучше всего было бы полностью забыть о термине «заголовок объекта» и говорить только о «класс», «слово-метка» и «пробел».

Holger 24.05.2018 17:36

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