DEV Community

valerialistratova
valerialistratova

Posted on

ActiveSerializer. Ultra-fast Java Serialization Library

ActiveSerializer is a modern bytecode generator of extremely fast and space-efficient serializers for data transferring. It's core features are ultimate performance and streamlined programming model. ActiveSerializer doesn't create redundant layers of data transfer objects and works straight with Java classes with the help of annotations.

ActiveSerializer is a really powerful tool:

  • Works directly with Java classes via annotations. No additional layers of intermediate DTO classes.
  • Implemented using runtime bytecode generation to be compatible with dynamically created classes.
  • Stable binary format with backward binary compatibility.
  • Support of the scheme evolution: changeable versions, added or removed fields, etc.
  • Can be easily extended - you can write your own plugins for specific classes.
  • Includes special hints for even more efficient code: String formats, nullable, varlen, etc.
  • Provides little endian format for JVM intrinsics.
  • Support of unsafe mode for the best performance.
  • Cyclic references of any complexity.
  • UTF-8, UTF-16 and ISO8859-1 encoding.
  • Compatible even with complex collections, generics and nullable values.

Performance

According to the ActiveSerializer website, it claims to be the fastest JVM-based serializer among the existing according to the results of a popular JVM Serializer benchmark tool.

Such performance of ActiveSerializer is a result of well-designed architecture and wide use of ActiveCodegen library for dynamic runtime code generation. Let's test ActiveSerializer out!

Simple Object Serialization

Let's create a serializable POJO using @Serialize and @Deserialze annotations:

public static class Person {
    //use @Deserialize annotation with property name in constructor
    public Person(@Deserialize("age") int age, @Deserialize("name") String name) {
        this.age = age;
        this.name = name;
    }

    // Use @Serialize annotation with order number on properties or their getters.
    // The name of the property must be the same as declared in @Deserialize annotation.
    // order number provides compatibility.
    @Serialize(order = 0)
    public int age;

    @Serialize(order = 1)
    public final String name;

    private String surname;

    @Serialize(order = 2)
    public String getSurname() {
        return surname;
    }

    public void setSurname(String surname) {
        this.surname = surname;
    }
}
Enter fullscreen mode Exit fullscreen mode

This is enough to make a class serializable. Now let's create a Person instance and a BinarySerializer (represents a serializer that will encode and decode <Person> values to byte arrays). So we will also need a byte array to store the result of the serialization.

Person john = new Person(34, "John");
john.setSurname("Smith");
byte[] buffer = new byte[200];
BinarySerializer<Person> serializer = SerializerBuilder.create(getSystemClassLoader())
    .build(Person.class);
Enter fullscreen mode Exit fullscreen mode

In order to encode and decode our instance, simply use encode and decode methods:

serializer.encode(buffer, 0, john);
Person johnCopy = serializer.decode(buffer, 0);
Enter fullscreen mode Exit fullscreen mode

Let's move on to something a little bit more complicated.

Generics and Interfaces Serialization

ActiveSerializer allows to work with interfaces and generics with a simple DSL. First, let's create a Skill class:

public static class Skill<K, V> {
    private K key;
    private V value;

    public Skill(@Deserialize("key") K key, @Deserialize("value") V value) {
        this.key = key;
        this.value = value;
    }

    @Serialize(order = 0)
    public K getKey() {
        return key;
    }

    @Serialize(order = 1)
    public V getValue() {
        return value;
    }
}
Enter fullscreen mode Exit fullscreen mode

Next, we'll create a Person interface. It will have a method that returns a list of skills:

public interface Person&lt;K, V> {
    @Serialize(order = 0)
    List<Skill<K, V>> getSkills();
}
Enter fullscreen mode Exit fullscreen mode

Now we need to create a class that will implement Person interface. Let it be a Developer:

public static class Developer implements Person<Integer, String> {
    private List<Skill<Integer, String>> list;

    @Serialize(order = 0)
    @Override
    public List<Skill<Integer, String>> getSkills() {
        return list;
    }

    public void setSkills(List<Skill<Integer, String>> list) {
        this.list = list;
    }
}
Enter fullscreen mode Exit fullscreen mode

Now create an instance of Developer, BinarySerializer<Developer>, and a byte array:

Developer developer = new Developer();
developer.setSkills(Arrays.asList(
    new Skill<>(1, "Java"),
    new Skill<>(2, "ActiveSerializer")));

byte[] buffer = new byte[200];
BinarySerializer<Developer> serializer = SerializerBuilder.create(getSystemClassLoader())
        .build(Developer.class);
Enter fullscreen mode Exit fullscreen mode

Now you can serialize and deserialize Developer instance with encode and decode methods:

serializer.encode(buffer, 0, developer);
Developer developer2 = serializer.decode(buffer, 0);
Enter fullscreen mode Exit fullscreen mode

Have any questions? You're welcome in the comment section!

Discussion (1)

Collapse
hogmuzzle profile image
hogmuzzle

In order to use this serializer one has to know the size of the buffer beforehand. It is not always possible.