Data size benefits: benchmark

What are the benefits of using Externalizable in terms of the serialized data size? We benchmark the data size benefits by comparing  Serializable versus Externalizable. We illustrate how the absence of class meta-data in the output allows a more efficient serialization with Externalizable classes compared to the standard serialization using Serializable.

We show the benefits of the Externalizable interface over the Serializable interface.  The Externalizable logic does not put meta-data into the serialized data. This meta-data contains a description of the serialized data. The absence of this overhead explains why classes that implement the Externalizable interface require less data when serialized.

It is important to note that the improvements we report here were obtained using Externalizer4J.  The java.io.Serializable classes were automatically converted into java.io.Externalizable classes without any coding. Externalizer4j analysed the bytecode and updated it to produce the most efficient implementation for the writeExternal() and readExternal() methods.  An example of these automatically generated methods can be found here

This benchmarks compares the improvement obtained over the default JDK serialization with Serializable. The result is a 39.9% reduction of the serialized size on average.

Serialized data size: how we measured

The internet abounds with comparisons as the one we are making here. In order to remove any room for interpretation we show the code use to measure the size of the serialized data.

The main points are:

  1. Use of a ByteArrayOutputStream to store resulting data. The content size of this stream gives use the final size
  2. Creation of a ObjectOutpuStream and writing of the object under test
  3. Flushing and closing to make sure all the data gets written to the output

 

    public int serializedSize(Object serializable) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(serializable);
        objectOutputStream.flush();
        objectOutputStream.close();
        return byteArrayOutputStream.size();
    }

 

Serialized size: Externalizable vs Serializable

 

Size comparison
Size (bytes)SerializableExternalizableGainSource
SerFinalField996930,3%#SerFinalField
ParentDataSerializable18613129,6%#ParentDataSerializable
ChildSerializable29319932,1%ChildSerializable
Field15Serializable31014553,2%#Field15Serializable
ChildChildSerializable46828339,5%#ChildChildSerializable
SelfReferencing1637454,6%#SelfReferencing

 

Class source code

Field15Serializable

import java.io.Serializable;
 
public class Field15Serializable implements Serializable {
    public Object o1 = "1";
    public Object o2 = "2";
    public Object o3 = "3";
    public Object o4 = "4";
    public Object o5 = "5";
    public Object o6 = "6";
    public Object o7 = "7";
    public Object o8 = "8";
    public Object o9 = "9";
    public Object o10 = "10";
    public Object o11 = "11";
    public Object o12 = "12";
    public Object o13 = "13";
    public Object o14 = "14";
    public Object o15 = "15";
 
[email protected]
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
 
        Field15Serializable that = (Field15Serializable) o;
 
        if (o1 != null ? !o1.equals(that.o1) : that.o1 != null) return false;
        if (o10 != null ? !o10.equals(that.o10) : that.o10 != null) return false;
        if (o11 != null ? !o11.equals(that.o11) : that.o11 != null) return false;
        if (o12 != null ? !o12.equals(that.o12) : that.o12 != null) return false;
        if (o13 != null ? !o13.equals(that.o13) : that.o13 != null) return false;
        if (o14 != null ? !o14.equals(that.o14) : that.o14 != null) return false;
        if (o15 != null ? !o15.equals(that.o15) : that.o15 != null) return false;
        if (o2 != null ? !o2.equals(that.o2) : that.o2 != null) return false;
        if (o3 != null ? !o3.equals(that.o3) : that.o3 != null) return false;
        if (o4 != null ? !o4.equals(that.o4) : that.o4 != null) return false;
        if (o5 != null ? !o5.equals(that.o5) : that.o5 != null) return false;
        if (o6 != null ? !o6.equals(that.o6) : that.o6 != null) return false;
        if (o7 != null ? !o7.equals(that.o7) : that.o7 != null) return false;
        if (o8 != null ? !o8.equals(that.o8) : that.o8 != null) return false;
        if (o9 != null ? !o9.equals(that.o9) : that.o9 != null) return false;
 
        return true;
    }
 
[email protected]
    public int hashCode() {
        int result = o1 != null ? o1.hashCode() : 0;
        result = 31 * result + (o2 != null ? o2.hashCode() : 0);
        result = 31 * result + (o3 != null ? o3.hashCode() : 0);
        result = 31 * result + (o4 != null ? o4.hashCode() : 0);
        result = 31 * result + (o5 != null ? o5.hashCode() : 0);
        result = 31 * result + (o6 != null ? o6.hashCode() : 0);
        result = 31 * result + (o7 != null ? o7.hashCode() : 0);
        result = 31 * result + (o8 != null ? o8.hashCode() : 0);
        result = 31 * result + (o9 != null ? o9.hashCode() : 0);
        result = 31 * result + (o10 != null ? o10.hashCode() : 0);
        result = 31 * result + (o11 != null ? o11.hashCode() : 0);
        result = 31 * result + (o12 != null ? o12.hashCode() : 0);
        result = 31 * result + (o13 != null ? o13.hashCode() : 0);
        result = 31 * result + (o14 != null ? o14.hashCode() : 0);
        result = 31 * result + (o15 != null ? o15.hashCode() : 0);
        return result;
    }
}

 

ParentDataSerializable

import java.io.Serializable;
import java.util.Date;

public class ParentDataSerializable implements Serializable {
public String string = "s";
public long l = 1L;
public Date d = new Date();

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

ParentDataSerializable that = (ParentDataSerializable) o;

if (l != that.l) return false;
if (d != null ? !d.equals(that.d) : that.d != null) return false;
if (string != null ? !string.equals(that.string) : that.string != null) return false;

return true;
}

@Override
public int hashCode() {
int result = string != null ? string.hashCode() : 0;
result = 31 * result + (int) (l ^ (l >>> 32));
result = 31 * result + (d != null ? d.hashCode() : 0);
return result;
}
}

 

ChildChildSerializable

import java.util.Date;

public class ChildChildSerializable extends ChildSerializable {
    public Date childChildDate = new Date();
    SerFieldSmall serFieldSmall;
}