GraalVM 序列化
GraalVM Native Image
GraalVM 的 native image 能将 Java 代码提前编译为本地代码,从而构建更快、更小、更精简的应用。
Native image 不包含 JIT 编译器,无法在运行时将字节码编译为机器码,也不支持反射,除非配置反射元数据文件。
Apache Fory™ 在 GraalVM native image 下运行良好。Fory 会在 graalvm 构建阶段为 Fory JIT framework 和 MethodHandle/LambdaMetafactory 生成所有序列化器代码,运行时直接使用这些生成的代码进行序列化,无需额外开销,性能优异。
在 graalvm native image 下使用 Fory 时,必须将 Fory 创建为类的静态字段,并在类初始化时注册所有类型。然后在 resources/META-INF/native-image/$xxx/native-image.properties 下配置 native-image.properties,告知 graalvm 在 native image 构建时初始化该类。例如,配置 org.apache.fory.graalvm.Example 类在构建时初始化:
Args = --initialize-at-build-time=org.apache.fory.graalvm.Example
使用 fory 的另一个好处是无需配置繁琐的 reflection json 和 serialization json。只需对每个需要序列化的类型调用 org.apache.fory.Fory.register(Class<?>, boolean) 即可。
注意:Fory 的 asyncCompilationEnabled 选项在 graalvm native image 下会自动禁用,因为 native image 运行时不支持 JIT。
非线程安全 Fory
示例:
import org.apache.fory.Fory;
import org.apache.fory.util.Preconditions;
import java.util.List;
import java.util.Map;
public class Example {
public record Record (
int f1,
String f2,
List<String> f3,
Map<String, Long> f4) {
}
static Fory fory;
static {
fory = Fory.builder().build();
// 注册并生成 序列化器代码。
fory.register(Record.class, true);
}
public static void main(String[] args) {
Record record = new Record(10, "abc", List.of("str1", "str2"), Map.of("k1", 10L, "k2", 20L));
System.out.println(record);
byte[] bytes = fory.serialize(record);
Object o = fory.deserialize(bytes);
System.out.println(o);
Preconditions.checkArgument(record.equals(o));
}
}
然后在 native-image.properties 配置中添加 org.apache.fory.graalvm.Example 的构建时初始化:
Args = --initialize-at-build-time=org.apache.fory.graalvm.Example
线程安全 Fory
import org.apache.fory.Fory;
import org.apache.fory.ThreadLocalFory;
import org.apache.fory.ThreadSafeFory;
import org.apache.fory.util.Preconditions;
import java.util.List;
import java.util.Map;
public class ThreadSafeExample {
public record Foo (
int f1,
String f2,
List<String> f3,
Map<String, Long> f4) {
}
static ThreadSafeFory fory;
static {
fory = new ThreadLocalFory(classLoader -> {
Fory f = Fory.builder().build();
// 注册并生成序列化器代码。
f.register(Foo.class, true);
return f;
});
}
public static void main(String[] args) {
System.out.println(fory.deserialize(fory.serialize("abc")));
System.out.println(fory.deserialize(fory.serialize(List.of(1,2,3))));
System.out.println(fory.deserialize(fory.serialize(Map.of("k1", 1, "k2", 2))));
Foo foo = new Foo(10, "abc", List.of("str1", "str2"), Map.of("k1", 10L, "k2", 20L));
System.out.println(foo);
byte[] bytes = fory.serialize(foo);
Object o = fory.deserialize(bytes);
System.out.println(o);
}
}
然后在 native-image.properties 配置中添加 org.apache.fory.graalvm.ThreadSafeExample 的构建时初始化:
Args = --initialize-at-build-time=org.apache.fory.graalvm.ThreadSafeExample
框架集成
对于框架开发者,如果希望集成 fory 作为序列化方案,可以提供一个配置文件,让用户列出所有需要序列化的类,然后加载这些类并在 Fory 集成类中调用 org.apache.fory.Fory.register(Class<?>, boolean) 进行注册,并配置该类在 graalvm native image 构建时初始化。