How to get amount of serialized bytes representing a Java object?
You can serialise each object into arrays and compare the length of each array. This is not very accurate, in the general case, but often gives a good approximation.
Have a look at ObjectOutputStream (which can be used to serialise an object and turn it into Bytes) and ByteArrayOutputStream (which can be used to hold the serialised bytes).
I don't think you've got much choice but to modify your code so that it measures the message sizes at runtime.
You could just serialize example objects and capture and measure the serialized size. This has the following problems:
- You can never be sure that the objects are typical.
- Various aggregation effects mean that it is hard to deduce the size of a message from the serialized size of its component objects. (For instance, class signatures are only encoded once per serialization.)
- This approach tells you nothing about the relative frequency of different message types.
If you can manage this, you will get more accurate results if you can measure the actual messages. This would most likely entail modifying the agent framework to count, measure and (ideally) classify messages into different kinds. The framework might already have hooks for doing this.
The method doesn't have to be dead-on accurate, as long as it scales proportionally to the actual size of the object. E.g. a Vector of strings of length 4 will report as larger than a Vector of strings of length 5.
(I assume that you meant smaller than ...)
Your example illustrates one of the problems of trying to estimate serialized object sizes. A serialization of a Vector<String>
of size 4 could be smaller ... or larger ... that a Vector<String>
of size 5. It depends on what the String values are. Additionally, if a message contains two Vector<String>
objects, the serialized size occupied by the vectors will be less that sum of the sizes of the two vectors when they are serialized separately.
I needed to check this accurately per-memcache write while investigating a server bug where memcache sizes were exceeded. To avoid the overhead of a big byte array for large objects I extended OutputStream as a counter:
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;
}
}
You can convert your object into a byte array using ObjectOutputStream
and 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;
}
I just tested this out. The object who's size you're trying to calculate, needs to implement Serializable
(which means you may have to mark every object as such simply to get its size. Might not be desirable). I wrote a quick and dirty program to test this out:
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;
}
}
Which gave me:
p1 85
p2 94
s1 17
s2 11
EDIT
Stephen C's answer highlights some caveats with this method.