kryo的使用,经常可以在网上看到这样的例子
private static Kryo kryo = new Kryo();
public byte[] serialize(Object o) {
Output output = new Output(new ByteArrayOutputStream());
kryo.writeClassAndObject(output, o);
byte[] bytes = output.toBytes();
output.flush();
output.close();
return bytes;
}
public <T> T deserialize(byte[] bytes) {
Input input = new Input(bytes);
T o = (T)kryo.readClassAndObject(input);
return o;
}
但这样实际有两个隐藏的问题。
-
Output output = new Output(new ByteArrayOutputStream());
会导致缓冲区溢出
public Output(OutputStream outputStream) {
this(4096, 4096);
if(outputStream == null) {
throw new IllegalArgumentException("outputStream cannot be null.");
} else {
this.outputStream = outputStream;
}
}
可以看到使用OutputStream构建Output时,缓冲区默认值和最大值都是4096。当遇到大对象,会缓冲区溢出,这是kryo会从开头重新写缓冲区,这样又导致另一个问题。
kryo中序列化的类要注册一个id,kryo序列化时,会将该id+2写入结果开头,作为class的标识。
kryo还提供了writeClassAndObject/readClassAndObject
的方式,会将class的信息写入到结果字节中,读取时也会从字节中读取class信息,如果class标识为1,表示使用的是writeClassAndObject/readClassAndObject
的方式。
回到缓冲区溢出的问题,kryo从开头重新写缓冲区,会导致class标识被重写为其他值,当读取时,kryo会使用该错误的class标识查找对应的注册class,查找失败会抛出class未注册的异常:
com.esotericsoftware.kryo.KryoException: Encountered unregistered class ID: -39
因为缓冲区溢出导致抛出class未注册的异常,这个就非常有迷惑性了,需要对kryo机制有一定了解才能解决。
-
private static Kryo kryo = new Kryo();
导致线程安全问题
Kryo不是线程安全的,使用该静态的Kryo类会在多线程情况出现各种问题。
为了避免频繁创建Kryo对象导致性能损耗,可以使用ThreadLocal或apache的commons-pool2连接池。
kryo是性能较高的序列化工具,可用于dubbo或netty的序列化性能,拓展:
使用Kryo的序列化方式提升Netty性能
网友评论