Почему Point занимает меньше памяти, чем Integer

#java #memory

#java #память

Вопрос:

Я измеряю размеры объектов, проверяя, как долго их байтовые массивы используют ByteArrayOutputStream. При выполнении:

 System.out.println(Utils.getObjectSize(new Integer(123123)));
System.out.println(Utils.getObjectSize(new Point(123, 123)));
  

Они возвращают 81 и 51.

Я полагаю, что Point состоит из двух примитивов, но, похоже, причина не в этом.

Код I для Utils.getObjectSize:

 ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);
objectStream.writeObject(object);
objectStream.close();
return byteStream.toByteArray().length;
  

Редактировать:

Я неправильно выразился. На самом деле я хотел знать, почему они занимают больший размер в потоке.

Комментарии:

1. Не могли бы вы, пожалуйста, показать, каков результат System.out.println(Utils.getObjectSize(new Point(123123, 123123))); ?

2. Размер новой точки (123123, 123123); также равен 51

Ответ №1:

Чтобы начать со строк java.lang.Integer , java.lang.Number и value появляются в результате сериализации для Integer . Это должно дать подсказку о том, почему размер сериализации не очень хорошо коррелирует с членами объекта.

Вот часть содержимого результирующих байтовых массивов, перечисленных:http://ideone.com/ragKZ

 import java.awt.Point;
import java.io.*;

class Test {

    public static byte[] getBytes(Object object) throws IOException {
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        ObjectOutputStream objectStream = new ObjectOutputStream(byteStream);
        objectStream.writeObject(object);
        objectStream.close();
        return byteStream.toByteArray(); 
    }

    public static void main(String[] args) throws IOException {

        byte[] iBytes = getBytes(new Integer(123123));
        System.out.println(new String(iBytes,  8, 17)); // "java.lang.Integer"
        System.out.println(new String(iBytes, 39,  5)); // "value"
        System.out.println(new String(iBytes, 48, 16)); // "java.lang.Number"
        // ...

        byte[] pBytes = getBytes(new Point(123, 123));
        System.out.println(new String(pBytes, 8, 14));  // "java.awt.Point"
        // ...
    }
}
  

Комментарии:

1. Вы получаете то же самое с java.awt.Point и java.awt.Point2D

2. Тогда отправка целого числа по потоку кажется плохой идеей? Должен ли я всегда создавать класс, содержащий примитивное целое число для потоков?

3. Если вы так заинтересованы в эффективной упаковке своих данных, я не думаю, что использование потоков объектов является рекомендуемым подходом. Хотя не могу сказать, что я очень опытен в этой области.

4. @Роб Фокс, Большая часть неэффективности не повторяется для каждого объекта. Отправка 10 целых чисел не в 10 раз больше. Однако, как предлагает @aioobe, если вы беспокоитесь о каждом байте, не используйте обычную сериализацию Java. Существует множество альтернатив.

5. @Rob Fox — Вы ищете способ эффективной сериализации классов с примитивными значениями?

Ответ №2:

Ваш подход к определению размера объектов на самом деле вообще не измеряет, сколько места они занимают в памяти.

Point на самом деле займет больше памяти, чем Integer (для JVM с выравниванием по 4 байта; при выравнивании по 8 байтам они будут одинаковыми) — просто происходит сериализация меньшего размера, вероятно, из-за иерархии наследования.

Комментарии:

1. Если у вас есть JVM с 8-байтовой границей для выделения, они будут одинакового размера. например, OpenJDK делает это.

Ответ №3:

Целое число больше для сериализации, потому что его родительский элемент Number сериализуем. Если вы напечатаете все текстовые байты в сериализации объекта, вы получите

 ....sr..java.lang.Integer.......8...I..valuexr..java.lang.Number...........xp....
  

Однако родительский элемент Point не сериализуем и не сериализуется.

 ....sr..java.awt.Point...r4~.amp;...I..xI..yxp...{...{
  

Также имя поля Integer длиннее (немного)

 byte[] bytes = byteStream.toByteArray();
for(int i=0;i<bytes.length;i  ) {
    char ch = (char) bytes[i];
    if (ch >= ' ' amp;amp; ch < 127)
        System.out.print(ch);
    else
        System.out.print('.');
}
System.out.println();
  

Используемый вами метод учитывает, насколько велика сериализация объекта в виде байта[], а не то, насколько он велик в памяти. Стандартная сериализация Java не очень эффективна.

Целое число, вероятно, будет потреблять 16 4 байта, а точка, вероятно, будет потреблять 16 2 * 4 байты. Однако, поскольку многие JVM выделяют на границе 8 байт, вы, вероятно, обнаружите, что они потребляют одинаковый объем памяти.

Причина, по которой Integer больше для сериализации, заключается в том, что Java сериализует не только объект, но и его родителей, включая перечисление того, что они собой представляют.

Комментарии:

1. Я исследовал эту проблему и получил тот же результат. Я не смог бы объяснить это лучше вас. Очень хороший ответ, Питер!

Ответ №4:

Вы измеряете размер объекта, когда он сериализуется в поток. Это не будет совпадать с размером объекта в памяти.