#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:
Вы измеряете размер объекта, когда он сериализуется в поток. Это не будет совпадать с размером объекта в памяти.