This post originated from an RSS feed registered with Java Buzz
by Talip Ozturk.
Original Post: Hazelcast Internals 2: Serialization
Feed Title: Shared Memory
Feed URL: http://www.jroller.com/talipozturk/feed/entries/rss
Feed Description: about java, jcache, jini, javaspaces, distributed data structures and a little bit of me and life.
All your distributed objects such as your key and value objects, objects you offer into distributed queue and your distributed callable/runnable objects have to be Serializable.
Hazelcast serializes all your objects into an instance of com.hazelcast.nio.Data. Data is the binary representation of an object and it holds list of java.nio.ByteBuffer instances which are reused. When Hazelcast serializes an object into Data, it first checks whether the object is an instance of well-known, optimizable object such as String, Long, Integer, byte[], ByteBuffer. If not, it then checks whether the object is an instance of com.hazelcast.nio.DataSerializable. If not, Hazelcast uses standard java serialization to convert the object into binary format. See com.hazelcast.nio.Serializer for details.
So for faster serialization, Hazelcast recommends to use of String, Long, Integer, byte[] objects or to implement com.hazelcast.nio.DataSerializable interface. Here is an example of a class implementing com.hazelcast.nio.DataSerializable interface.
public class Address implements com.hazelcast.nio.DataSerializable {
private String street;
private int zipCode;
private String city;
private String state;
public Address() {}
//getters setters..
public void writeData(DataOutput out) throws IOException {
out.writeUTF(street);
out.writeInt(zipCode);
out.writeUTF(city);
out.writeUTF(state);
}
public void readData (DataInput in) throws IOException {
street = in.readUTF();
zipCode = in.readInt();
city = in.readUTF();
state = in.readUTF();
}
}
Lets take a look at another example which is encapsulating a DataSerializable field.
public class Employee implements com.hazelcast.nio.DataSerializable {
private String firstName;
private String lastName;
private int age;
private double salary;
private Address address; //address itself is DataSerializable
public Employee() {}
//getters setters..
public void writeData(DataOutput out) throws IOException {
out.writeUTF(firstName);
out.writeUTF(lastName);
out.writeInt(age);
out.writeDouble (salary);
address.writeData (out);
}
public void readData (DataInput in) throws IOException {
firstName = in.readUTF();
lastName = in.readUTF();
age = in.readInt();
salary = in.readDouble();
address = new Address();
// since Address is DataSerializable let it read its own internal state
address.readData (in);
}
}
As you can see, since address field itself is DataSerializable, it is calling address.writeData(out) when writing and address.readData(in) when reading.
Hazelcast serialization uses com.hazelcast.nio.BuffersOutputStream and com.hazelcast.nio.BuffersInputStream classes. These stream classes are using a pool of java.nio.ByteBuffer instances. Reusing ByteBuffer objects enables better memory management and it is very important for Hazelcast's performance. So as soon as Hazelcast is internally done with a Data object , which holds a list of ByteBuffer, it will return the ByteBuffer instances back to the ByteBuffer pool.
Hazelcast has a special object pool implementation that is thread-aware (see com.hazelcast.impl.ThreadContext.ObjectPool). When an object is returned to the ObjectPool, it is first trying to put it in thread-scoped queue, which is lock-free, if the queue is full then the object is offered to the thread-safe global queue. Since thread-scoped queue is local to the currently running thread, it is lock-free and super fast. When an object is polled from the ObjectPool, thread-scoped queue, which is local the current thread, is consumed first. If it is empty then the thread-safe global queue is consumed.