Performance Comparison of Java Object Serialization Methods Using Chronicle Queue
This article compares Java object serialization approaches—including default Serializable, explicit SelfDescribingMarshallable, and trivially copyable techniques—by benchmarking them with JMH and Chronicle Queue, showing that explicit serialization is roughly twice as fast as default and trivially copyable is over ten times faster.
Data Transfer Object
The article defines a FunData class as a Data Transfer Object (DTO) containing many primitive fields (long, double) to be used in the serialization experiments.
Default Serialization
Java's built‑in Serializable interface serializes objects via ObjectOutputStream and ObjectInputStream, relying on reflection to discover non‑transient fields and read/write each field, which can be performance‑heavy.
Chronicle Queue can also handle Serializable objects but offers a faster alternative through the abstract class SelfDescribingMarshallable, which still uses reflection but with lower CPU and garbage‑collection overhead.
Typical steps of default serialization are:
Identify non‑transient fields via reflection.
Read/write the identified fields via reflection.
Write the field values to the target binary format.
Example class using the default approach:
public final class DefaultFunData extends FunData {}Explicit Serialization
Implementing private readObject and writeObject methods gives full control over the serialization process, avoiding reflection and improving speed, though any new field must be manually handled. SelfDescribingMarshallable provides a similar capability without requiring those private methods, offering two concepts: a flexible Chronicle Wire format (binary, text, YAML, JSON) and an implicit binary format for high performance.
Example of explicit serialization:
public final class ExplicitFunData extends FunData {
@Override
public void readMarshallable(BytesIn bytes) {
securityId = bytes.readLong();
time = bytes.readLong();
bidQty0 = bytes.readDouble();
// ... read remaining fields ...
askPrice3 = bytes.readDouble();
}
@Override
public void writeMarshallable(BytesOut bytes) {
bytes.writeLong(securityId);
bytes.writeLong(time);
bytes.writeDouble(bidQty0);
// ... write remaining fields ...
bytes.writeDouble(askPrice3);
}
}Trivially Copyable
Because FunData contains only primitive fields, the JVM can lay them out contiguously in memory. By using Unsafe and memory‑copy operations, the whole object can be copied in a single step, bypassing per‑field access.
Chronicle Queue and Chronicle Bytes expose utilities to obtain the start offset and length of a trivially copyable object, enabling fast unsafe reads/writes.
Example of a trivially copyable DTO:
import static net.openhft.chronicle.bytes.BytesUtil.*;
public final class TriviallyCopyableFunData extends FunData {
static final int START = triviallyCopyableStart(TriviallyCopyableFunData.class);
static final int LENGTH = triviallyCopyableLength(TriviallyCopyableFunData.class);
@Override
public void readMarshallable(BytesIn bytes) {
bytes.unsafeReadObject(this, START, LENGTH);
}
@Override
public void writeMarshallable(BytesOut bytes) {
bytes.unsafeWriteObject(this, START, LENGTH);
}
}Benchmark
The article uses JMH to benchmark serialization and deserialization of the three FunData variants on a MacBook Pro (16‑inch, 2019) with JDK 1.8.0_312 and a 2.3 GHz 8‑core Intel Core i9.
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(NANOSECONDS)
@Fork(value = 1, warmups = 1)
@Warmup(iterations = 5, time = 200, timeUnit = MILLISECONDS)
@Measurement(iterations = 5, time = 500, timeUnit = MILLISECONDS)
public class BenchmarkRunner {
private final FunData defaultFunData = new DefaultFunData();
private final FunData explicitFunData = new ExplicitFunData();
private final FunData triviallyCopyableFunData = new TriviallyCopyableFunData();
private final Bytes<Void> toBytes = Bytes.allocateElasticDirect();
private final Bytes<Void> fromBytesDefault = Bytes.allocateElasticDirect();
private final Bytes<Void> fromBytesExplicit = Bytes.allocateElasticDirect();
private final Bytes<Void> fromBytesTriviallyCopyable = Bytes.allocateElasticDirect();
public BenchmarkRunner() {
defaultFunData.writeMarshallable(fromBytesDefault);
explicitFunData.writeMarshallable(fromBytesExplicit);
triviallyCopyableFunData.writeMarshallable(fromBytesTriviallyCopyable);
}
@Benchmark
public void defaultWrite() {
toBytes.writePosition(0);
defaultFunData.writeMarshallable(toBytes);
}
@Benchmark
public void defaultRead() {
fromBytesDefault.readPosition(0);
defaultFunData.readMarshallable(fromBytesDefault);
}
@Benchmark
public void explicitWrite() {
toBytes.writePosition(0);
explicitFunData.writeMarshallable(toBytes);
}
@Benchmark
public void explicitRead() {
fromBytesExplicit.readPosition(0);
explicitFunData.readMarshallable(fromBytesExplicit);
}
@Benchmark
public void trivialWrite() {
toBytes.writePosition(0);
triviallyCopyableFunData.writeMarshallable(toBytes);
}
@Benchmark
public void trivialRead() {
fromBytesTriviallyCopyable.readPosition(0);
triviallyCopyableFunData.readMarshallable(fromBytesTriviallyCopyable);
}
}Results (average time, nanoseconds per operation):
BenchmarkRunner.defaultRead avgt 5 88.772 ± 1.766 ns/op
BenchmarkRunner.defaultWrite avgt 5 90.679 ± 2.923 ns/op
BenchmarkRunner.explicitRead avgt 5 32.419 ± 2.673 ns/op
BenchmarkRunner.explicitWrite avgt 5 38.048 ± 0.778 ns/op
BenchmarkRunner.trivialRead avgt 5 7.437 ± 0.339 ns/op
BenchmarkRunner.trivialWrite avgt 5 7.911 ± 0.431 ns/opThus, explicit serialization is roughly twice as fast as the default Java serialization, while the trivially copyable approach is about four times faster than explicit and more than ten times faster than the default method.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
