Как получить количество сериализованных байтов, представляющих объект Java?

Какой синтаксис я использовал бы, чтобы получить количество байтов, представляющих строку, и сравнить их с количеством байтов, представляющих ArrayList, содержащих эту строку, например?

Я использую многоагентную агентную систему для отправки объектов через сообщения, и я хочу отслеживать, сколько места занимает каждое сообщение. Метод не должен быть мертвым, если он масштабируется пропорционально фактическому размеру объекта. Например. Вектор строк длины 4 будет сообщать как меньший, чем вектор строк длины 5.

Ответы

Ответ 1

Вы можете преобразовать свой объект в массив байтов, используя ObjectOutputStream и ByteArrayOutputStream:

public static int sizeof(Object obj) throws IOException {

    ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
    ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);

    objectOutputStream.writeObject(obj);
    objectOutputStream.flush();
    objectOutputStream.close();

    return byteOutputStream.toByteArray().length;
}

Я только что проверил это. Объект, размер которого вы пытаетесь вычислить, должен реализовать Serializable (это означает, что вам, возможно, придется отмечать каждый объект как таковой просто получить его размер. Не может быть желательно). Я написал быструю и грязную программу, чтобы проверить это:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Sizeof {

    public static class Person implements Serializable {
        private String name;
        private String age;

        public Person(String name, String age) {
            this.name = name;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getAge() {
            return age;
        }

        public void setAge(String age) {
            this.age = age;
        }
    }

    public static void main(String[] args) {
        Person p1 = new Person("Alby", "20");
        Person p2 = new Person("VeryLongName", "100");
        String s1 = "This is it";
        String s2 = "This";

        try {
            System.out.println("p1 " + sizeof(p1));
            System.out.println("p2 " + sizeof(p2));
            System.out.println("s1 " + sizeof(s1));
            System.out.println("s2 " + sizeof(s2));                                 
        }

        catch(Exception e) {
            e.printStackTrace();
        }
    }

    public static int sizeof(Object obj) throws IOException {

        ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteOutputStream);

        objectOutputStream.writeObject(obj);
        objectOutputStream.flush();
        objectOutputStream.close();

        return byteOutputStream.toByteArray().length;
    }
}

Который дал мне:

p1 85
p2 94
s1 17
s2 11

ИЗМЕНИТЬ

Ответ Стивена C подчеркивает некоторые предостережения с помощью этого метода.

Ответ 2

Мне нужно было проверить эту точную запись на memcache, исследуя ошибку сервера, где были превышены размеры memcache. Чтобы избежать накладных расходов большого массива байтов для больших объектов, я расширил OutputStream в качестве счетчика:

public class CheckSerializedSize extends OutputStream {

    /** Serialize obj and count the bytes */
    public static long getSerializedSize(Serializable obj) {
        try {
            CheckSerializedSize counter = new CheckSerializedSize();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(counter);
            objectOutputStream.writeObject(obj);
            objectOutputStream.close();
            return counter.getNBytes();
        } catch (Exception e) {
            // Serialization failed
            return -1;
        }
    }

    private long nBytes = 0;

    private CheckSerializedSize() {}

    @Override
    public void write(int b) throws IOException {
        ++nBytes;
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        nBytes += len;
    }

    public long getNBytes() {
        return nBytes;
    }
}

Ответ 3

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

Посмотрите на ObjectOutputStream (который можно использовать для сериализации объекта и преобразования его в байты) и ByteArrayOutputStream (который может использоваться для хранения сериализованных байтов).

Ответ 4

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

Вы можете просто сериализовать объекты-примеры и захватить и измерить сериализованный размер. Это имеет следующие проблемы:

  • Вы никогда не можете быть уверены, что объекты типичны.
  • Различные эффекты агрегирования означают, что трудно выводить размер сообщения из сериализованного размера его компонентных объектов. (Например, подписи классов кодируются только один раз для сериализации.)
  • Этот подход ничего не говорит о относительной частоте различных типов сообщений.

Если вы справитесь с этим, вы получите более точные результаты, если сможете измерить фактические сообщения. Это, скорее всего, влечет за собой изменение структуры агента для подсчета, измерения и (в идеале) классификации сообщений на разные типы. Возможно, в этой структуре уже есть крючки для этого.

Метод не должен быть мертвым, если он масштабируется пропорционально фактическому размеру объекта. Например. Вектор строк длины 4 будет сообщать как больше, чем вектор строк длины 5.

(Я предполагаю, что вы имели в виду меньше, чем...)

Ваш пример иллюстрирует одну из проблем, связанных с попыткой оценить размер сериализованных объектов. Сериализация Vector<String> размера 4 может быть меньше... или больше..., что a Vector<String> размера 5. Это зависит от того, что значения String. Кроме того, если сообщение содержит два объекта Vector<String>, сериализованный размер, занимаемый векторами, будет меньше этой суммы размеров двух векторов при их сериализации отдельно.

Ответ 6

Вы можете проверить размер объекта после процесса сериализации, используя Apache Commons, следующим образом:

    // Create serialize objects.
    final List<String> src = new ArrayList<String>();
    src.add("awsome");
    src.add("stack");
    src.add("overflow");

    System.out.println(
            "Size after serialization:" + SerializationUtils.serialize((Serializable) src).length);

Вывод:

Size after serialization:86