Skip to main content
Version: dev

Java Serialization Guide

Apache Fory™ provides blazingly fast Java object serialization with JIT compilation and zero-copy techniques. When only Java object serialization is needed, this mode delivers better performance compared to cross-language object graph serialization.

Features

High Performance

  • JIT Code Generation: Highly-extensible JIT framework generates serializer code at runtime using async multi-threaded compilation, delivering 20-170x speedup through:
    • Inlining variables to reduce memory access
    • Inlining method calls to eliminate virtual dispatch overhead
    • Minimizing conditional branching
    • Eliminating hash lookups
  • Zero-Copy: Direct memory access without intermediate buffer copies; row format supports random access and partial serialization
  • Variable-Length Encoding: Optimized compression for integers, longs
  • Meta Sharing: Cached class metadata reduces redundant type information
  • SIMD Acceleration: Java Vector API support for array operations (Java 16+)

Drop-in Replacement

  • 100% JDK Serialization Compatible: Supports writeObject/readObject/writeReplace/readResolve/readObjectNoData/Externalizable
  • Java 8-24 Support: Works across all modern Java versions including Java 17+ records
  • GraalVM Native Image: AOT compilation support without reflection configuration

Advanced Features

  • Reference Tracking: Automatic handling of shared and circular references
  • Schema Evolution: Forward/backward compatibility for class schema changes
  • Polymorphism: Full support for inheritance hierarchies and interfaces
  • Deep Copy: Efficient deep cloning of complex object graphs with reference preservation
  • Security: Class registration and configurable deserialization policies

Quick Start

Note that Fory creation is not cheap, the Fory instances should be reused between serializations instead of creating it every time. You should keep Fory as a static global variable, or instance variable of some singleton object or limited objects.

Single-Thread Usage

import java.util.List;
import java.util.Arrays;

import org.apache.fory.*;
import org.apache.fory.config.*;

public class Example {
public static void main(String[] args) {
SomeClass object = new SomeClass();
// Note that Fory instances should be reused between
// multiple serializations of different objects.
Fory fory = Fory.builder().withLanguage(Language.JAVA)
.requireClassRegistration(true)
.build();
// Registering types can reduce class name serialization overhead, but not mandatory.
// If class registration enabled, all custom types must be registered.
// Registration order must be consistent if id is not specified
fory.register(SomeClass.class);
byte[] bytes = fory.serialize(object);
System.out.println(fory.deserialize(bytes));
}
}

Multi-Thread Usage

import java.util.List;
import java.util.Arrays;

import org.apache.fory.*;
import org.apache.fory.config.*;

public class Example {
public static void main(String[] args) {
SomeClass object = new SomeClass();
// Note that Fory instances should be reused between
// multiple serializations of different objects.
ThreadSafeFory fory = new ThreadLocalFory(classLoader -> {
Fory f = Fory.builder().withLanguage(Language.JAVA)
.withClassLoader(classLoader).build();
f.register(SomeClass.class, 1);
return f;
});
byte[] bytes = fory.serialize(object);
System.out.println(fory.deserialize(bytes));
}
}

Fory Instance Reuse Pattern

import java.util.List;
import java.util.Arrays;

import org.apache.fory.*;
import org.apache.fory.config.*;

public class Example {
// reuse fory.
private static final ThreadSafeFory fory = new ThreadLocalFory(classLoader -> {
Fory f = Fory.builder().withLanguage(Language.JAVA)
.withClassLoader(classLoader).build();
f.register(SomeClass.class, 1);
return f;
});

public static void main(String[] args) {
SomeClass object = new SomeClass();
byte[] bytes = fory.serialize(object);
System.out.println(fory.deserialize(bytes));
}
}

Thread Safety

Fory provides multiple options for thread-safe serialization:

ThreadLocalFory

Uses thread-local storage to maintain separate Fory instances per thread:

ThreadSafeFory fory = new ThreadLocalFory(classLoader -> {
Fory f = Fory.builder().withLanguage(Language.JAVA)
.withClassLoader(classLoader).build();
f.register(SomeClass.class, 1);
return f;
});
byte[] bytes = fory.serialize(object);
System.out.println(fory.deserialize(bytes));

Virtual Threads

For JDK 21+ virtual-thread workloads, use buildVirtualThreadSafeFory(...):

ThreadSafeFory fory = Fory.builder()
.withLanguage(Language.JAVA)
.withRefTracking(false)
.withCompatibleMode(CompatibleMode.SCHEMA_CONSISTENT)
.withAsyncCompilation(true)
.buildVirtualThreadSafeFory();

See more details in Virtual Threads.

ThreadSafeForyPool

For environments where thread-local storage is not appropriate and you need the existing time-expiring pooled runtime, use buildThreadSafeForyPool:

ThreadSafeFory fory = Fory.builder()
.withLanguage(Language.JAVA)
.withRefTracking(false)
.withCompatibleMode(CompatibleMode.SCHEMA_CONSISTENT)
.withAsyncCompilation(true)
.buildThreadSafeForyPool(minPoolSize, maxPoolSize);

Note that calling buildThreadSafeFory() on ForyBuilder creates a ThreadLocalFory. This is not a good default for virtual-thread workloads because each virtual thread can create its own Fory instance. For virtual threads, prefer buildVirtualThreadSafeFory(...).

Builder Methods

// Single-thread Fory
Fory fory = Fory.builder()
.withLanguage(Language.JAVA)
.withRefTracking(false)
.withCompatibleMode(CompatibleMode.SCHEMA_CONSISTENT)
.withAsyncCompilation(true)
.build();

// Thread-safe Fory (ThreadLocalFory)
ThreadSafeFory fory = Fory.builder()
.withLanguage(Language.JAVA)
.withRefTracking(false)
.withCompatibleMode(CompatibleMode.SCHEMA_CONSISTENT)
.withAsyncCompilation(true)
.buildThreadSafeFory();

Next Steps