五种常见序列化算法:JDK、JSON、Hession、Kryo、ProtoStuff

反序列化:将二进制字节流转换成对象的过程。

Java中对象都是存储在内存中,准确地说是JVM的堆或栈内存中,可以各个线程之间进行对象传输,但是无法在进程之间进行传输。如果涉及到跨内存的数据传输(比如两台机器的传输),直接把对象作为参数传递就不可取了,这时就需要通过“网络”将数据传输。

JDK

JDK的序列化方法使用简单,只需要实现Serializable接口即可,这个接口的作用只是表明这个类是可以被序列化的。

每个序列化类都有一个serialVersionID,如果不手动指定编译器也会动态生成。反序列化时JVM会根据serialVersionID找指定版本的class文件进行反序列化。

优缺点:

JDK序列化会把对象类的描述和所有属性的元数据都序列化为字节流,另外继承的元数据也会序列化,所以导致序列化的元素较多且字节流很大,但是由于序列化了所有信息所以相对而言更可靠。

优点:

  • 使用简单,只需要在要序列化的类中实现Serializable接口即可。
  • 可靠,jdk序列化会把对象类的描述和所有属性的元数据也序列化成字节流,所以信息可靠。

缺点:

  • 不支持跨语言,因为是jdk自带的。
  • 性能差,要序列化的信息太多导致序列化后的二进制流太大,传输成本大。
  • 存在安全问题,序列化后的结果存在类的信息,如果攻击者构造一些恶意信息,会让反序列化产生一些非预期的对象。
public class JdkSerialization implements Serialization {
    @Override
    public <T> byte[] serialize(T object) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            return baos.toByteArray();
        } catch (IOException e) {
            throw new SerializeException("Jdk serialize failed.", e);
        }
    }

    @Override
    public <T> T deserialize(Class<T> clazz, byte[] bytes) {
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            ObjectInputStream ois = new ObjectInputStream(bais);
            return (T) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new SerializeException("Jdk deserialize failed.", e);
        }
    }
}

JSON

JSON使用的是Gson实现,此外还可以使用FastJson、Jackson等实现JSON序列化。

优点:

  • 可读性好:JSON使用文本格式,以键值对的形式表示对象和数据,易于阅读和调试。
  • 广泛支持:几乎所有编程语言都有JSON的解析和生成库,方便各种应用之间的数据交换。

缺点:

  • 性能较低:相对于二进制格式的序列化算法,JSON的序列化和反序列化速度较慢。
  • 存储空间相对较大:相比其他算法,JSON产生的序列化数据通常比较冗余,占用较大的存储空间。
public class JsonSerialization implements Serialization {

    /**
     * 自定义 JavClass 对象序列化,解决 Gson 无法序列化 Class 信息
     */
    static class ClassCodec implements JsonSerializer<Class<?>>, JsonDeserializer<Class<?>> {
        @SneakyThrows
        @Override
        public Class<?> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
            String name = json.getAsString();
            return Class.forName(name);
        }

        @Override
        public JsonElement serialize(Class<?> src, Type typeOfSrc, JsonSerializationContext context) {
            // class -> json
            return new JsonPrimitive(src.getName());
        }
    }

    @Override
    public <T> byte[] serialize(T object) {
        try {
            Gson gson = new GsonBuilder().registerTypeAdapter(Class.class, new ClassCodec()).create();
            String json = gson.toJson(object);
            return json.getBytes(StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new SerializeException("Json serialize failed.", e);
        }
    }

    @Override
    public <T> T deserialize(Class<T> clazz, byte[] bytes) {
        try {
            Gson gson = new GsonBuilder().registerTypeAdapter(Class.class, new ClassCodec()).create();
            String json = new String(bytes, StandardCharsets.UTF_8);
            return gson.fromJson(json, clazz);
        } catch (JsonSyntaxException e) {
            throw new SerializeException("Json deserialize failed.", e);
        }
    }
}

Hession

Hession和JDK自带的序列化方式类似,Hessian采用的也是二进制协议,只不过Hessian序列化之后,字节数更小,性能更优。

优点:

  • 跨语言支持:Hessian可以在多种编程语言之间进行序列化和反序列化,方便实现跨平台的通信。
  • 性能比jdk好,因为序列化的内容更少,例如如果该对象出现过就不会重新序列化,而是表示它的引用位置。并且序列化的格式更简洁。还有不会序列化属性的元数据等信息。

缺点:

  • 性能相对较低:与Kryo和Protostuff相比,Hessian的性能稍差一些,尤其是在处理复杂对象和大数据量时。
public class HessianSerialization implements Serialization {
    @Override
    public <T> byte[] serialize(T object) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            HessianSerializerOutput hso = new HessianSerializerOutput(baos);
            hso.writeObject(object);
            hso.flush();
            return baos.toByteArray();
        } catch (IOException e) {
            throw new SerializeException("Hessian serialize failed.", e);
        }
    }

    @Override
    public <T> T deserialize(Class<T> clazz, byte[] bytes) {
        try {
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
            HessianSerializerInput hsi = new HessianSerializerInput(bis);
            return (T) hsi.readObject();
        } catch (IOException e) {
            throw new SerializeException("Hessian deserialize failed.", e);
        }
    }

}

Kroy

号称Java 中最快的序列化框架。

Kryo 为了提供性能和减小序列化结果体积,提供注册的序列化对象类的方式。在注册时,会为该序列化类生成 int ID,后续在序列化时使用 int ID 唯一标识该类型。

Kryo 不是线程安全的。每个线程都应该有自己的 Kryo 对象、输入和输出实例。因此在多线程环境中,可以考虑使用 ThreadLocal 或者对象池来保证线程安全性。

优点:

  • 高性能:Kryo在序列化和反序列化过程中具有很高的性能,尤其在处理复杂对象和大数据量时表现 出色。
  • 体积小:Kryo生成的序列化数据通常比其他算法更紧凑,占用较小的存储空间。
  • 兼容性:Kryo可以处理添加、删除或修改类定义的情况,并能与旧版本的数据进行兼容。

缺点:

  • 不跨语言:Kryo是一种Java特定的序列化框架,不支持跨语言序列化。
  • 可读性差:Kryo使用二进制格式,无法以人类可读的形式展示序列化数据。
public class KryoSerialization implements Serialization {

    // kryo 线程不安全,所以使用 ThreadLocal 保存 kryo 对象
    private final ThreadLocal<Kryo> kryoThreadLocal = ThreadLocal.withInitial(() -> {
        Kryo kryo = new Kryo();
        kryo.register(RpcRequest.class);
        kryo.register(RpcResponse.class);
        return kryo;
    });

    @Override
    public <T> byte[] serialize(T object) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            Output output = new Output(baos);
            Kryo kryo = kryoThreadLocal.get();
            // 将对象序列化为 byte 数组
            kryo.writeObject(output, object);
            kryoThreadLocal.remove();
            return output.toBytes();
        } catch (Exception e) {
            throw new SerializeException("Kryo serialize failed.", e);
        }
    }

    @Override
    public <T> T deserialize(Class<T> clazz, byte[] bytes) {
        try {
            ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
            Input input = new Input(bais);
            Kryo kryo = kryoThreadLocal.get();
            // 将 byte 数组反序列化为 T 对象
            T object = kryo.readObject(input, clazz);
            kryoThreadLocal.remove();
            return object;
        } catch (Exception e) {
            throw new SerializeException("Kryo deserialize failed.", e);
        }
    }
}

ProtoStuff

Protostuff使用schema来描述java对象的结构信息,包括字段名称、类型等。schema可以通过编译.proto文件生成,可以通过运行时动态生成。

在序列化时,根据schema将对象转换成二进制格式。

在反序列化时,根据schema将二进制转换成对象。读取二进制字节流的信息,根据schema完成解析和赋值。

优点:

  • 高性能:protoStuff使用紧凑的二进制编码,序列化后的字节数量更少。
  • 跨语言支持:Protostuff支持多种编程语言,并且在不同语言之间的序列化和反序列化效果相同。

缺点:

  • 依赖schema:Protostuff需要通过schema来描述对象的结构信息,如果schema不正确则不能序列化成功。
  • 如果java对象的字段发生变化时,会导致旧版本的字节流无法正常反序列化。
public class ProtostuffSerialization implements Serialization {

    /**
     * 提前分配好 Buffer,避免每次进行序列化都需要重新分配 buffer 内存空间
     */
    private final LinkedBuffer BUFFER = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);

    @SuppressWarnings({"unchecked", "rawtypes"})
    @Override
    public <T> byte[] serialize(T object) {
        try {
            Schema schema = RuntimeSchema.getSchema(object.getClass());
            return ProtostuffIOUtil.toByteArray(object, schema, BUFFER);
        } catch (Exception e) {
            throw new SerializeException("Protostuff serialize failed.", e);
        } finally {
            // 重置 buffer
            BUFFER.clear();
        }
    }

    @Override
    public <T> T deserialize(Class<T> clazz, byte[] bytes) {
        try {
            Schema<T> schema = RuntimeSchema.getSchema(clazz);
            T object = schema.newMessage();
            ProtostuffIOUtil.mergeFrom(bytes, object, schema);
            return object;
        } catch (Exception e) {
            throw new SerializeException("Protostuff deserialize failed.", e);
        }
    }
}

总结

序列化算法

优点

缺点

JDK

使用简单,可靠

性能极差,占空间,不跨语言

JSON

格式简洁、可读性强

性能较差,占空间

Hession

跨平台方便

性能较差

Kryo

性能高,占空间小

不跨语言

ProtoStuff

高性能

依赖schema

性能对比:Kryo > ProtoStuff > JSON > Hession > JDK

空间开销:JKD > Hession > JSON > ProtoStuff > Kroy

以上数据来自https://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking

  • 21
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值