我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Java Kryo,Protostuff,Hessian序列化方式对比

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Java Kryo,Protostuff,Hessian序列化方式对比

前言

前段时间在写RPC框架的时候用到了Kryo、Hessian、Protostuff三种序列化方式。但是当时因为急于实现功能,就只是简单的的看了一下如何使用这三种序列化方式,并没有去深入研究各自的特性,以及优点和缺点。知道现在就将RPC框架写完了之后,才有时间静下心来对三种方式做一个对比,总结。

Kryo、Hessain、Protostuff都是第三方开源的序列化/反序列化框架,要了解其各自的特性,我们首先需要知道序列化/反序列化是什么:

序列化:就是将对象转化成字节序列的过程。

反序列化:就是讲字节序列转化成对象的过程。

seriallization 序列化 : 将对象转化为便于传输的格式, 常见的序列化格式:二进制格式,字节数组,json字符串,xml字符串。

deseriallization 反序列化:将序列化的数据恢复为对象的过程

如果对序列化相关概念还不是很清楚的同学可以参照美团技术团队的序列化和反序列化

笔记参考图:

image-20220702131833644

性能对比

前期准备

  • 我们先创建一个新的Maven项目
  • 然后导入依赖
<dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-api</artifactId>
    <version>5.8.2</version>
    <scope>test</scope>
</dependency>
<!-- 代码简化 -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.20</version>
</dependency>
<!--kryo-->
<dependency>
    <groupId>com.esotericsoftware</groupId>
    <artifactId>kryo-shaded</artifactId>
    <version>4.0.2</version>
</dependency>
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.10</version>
</dependency>
<!--protostuff-->
<dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-core</artifactId>
    <version>1.7.2</version>
</dependency>
<dependency>
    <groupId>io.protostuff</groupId>
    <artifactId>protostuff-runtime</artifactId>
    <version>1.7.2</version>
</dependency>
<!--hessian2-->
<dependency>
    <groupId>com.caucho</groupId>
    <artifactId>hessian</artifactId>
    <version>4.0.62</version>
</dependency>

工具类:

kryo

package cuit.pymjl.utils;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import org.apache.commons.codec.binary.Base64;
import org.objenesis.strategy.StdInstantiatorStrategy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;

@SuppressWarnings("all")
public class KryoUtils {
    private static final String DEFAULT_ENCODING = "UTF-8";
​
    //每个线程的 Kryo 实例
    private static final ThreadLocal<Kryo> KRYO_LOCAL = new ThreadLocal<Kryo>() {
        @Override
        protected Kryo initialValue() {
            Kryo kryo = new Kryo();
​
            
            //支持对象循环引用(否则会栈溢出)
            kryo.setReferences(true); //默认值就是 true,添加此行的目的是为了提醒维护者,不要改变这个配置
​
            //不强制要求注册类(注册行为无法保证多个 JVM 内同一个类的注册编号相同;而且业务系统中大量的 Class 也难以一一注册)
            kryo.setRegistrationRequired(false); //默认值就是 false,添加此行的目的是为了提醒维护者,不要改变这个配置
​
            //Fix the NPE bug when deserializing Collections.
            ((Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy())
                    .setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());
​
            return kryo;
        }
    };
    
    public static Kryo getInstance() {
        return KRYO_LOCAL.get();
    }
​
    //-----------------------------------------------
    //          序列化/反序列化对象,及类型信息
    //          序列化的结果里,包含类型的信息
    //          反序列化时不再需要提供类型
    //-----------------------------------------------
​
    
    public static <T> byte[] writeToByteArray(T obj) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Output output = new Output(byteArrayOutputStream);
​
        Kryo kryo = getInstance();
        kryo.writeClassAndObject(output, obj);
        output.flush();
​
        return byteArrayOutputStream.toByteArray();
    }
    
    public static <T> String writeToString(T obj) {
        try {
            return new String(Base64.encodeBase64(writeToByteArray(obj)), DEFAULT_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }
    
    @SuppressWarnings("unchecked")
    public static <T> T readFromByteArray(byte[] byteArray) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
        Input input = new Input(byteArrayInputStream);
​
        Kryo kryo = getInstance();
        return (T) kryo.readClassAndObject(input);
    }
    
    public static <T> T readFromString(String str) {
        try {
            return readFromByteArray(Base64.decodeBase64(str.getBytes(DEFAULT_ENCODING)));
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }
    //-----------------------------------------------
    //          只序列化/反序列化对象
    //          序列化的结果里,不包含类型的信息
    //-----------------------------------------------
​
    
    public static <T> byte[] writeObjectToByteArray(T obj) {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Output output = new Output(byteArrayOutputStream);
        Kryo kryo = getInstance();
        kryo.writeObject(output, obj);
        output.flush();
        return byteArrayOutputStream.toByteArray();
    }
​
    
    public static <T> String writeObjectToString(T obj) {
        try {
            return new String(Base64.encodeBase64(writeObjectToByteArray(obj)), DEFAULT_ENCODING);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }
    
    @SuppressWarnings("unchecked")
    public static <T> T readObjectFromByteArray(byte[] byteArray, Class<T> clazz) {
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArray);
        Input input = new Input(byteArrayInputStream);
​
        Kryo kryo = getInstance();
        return kryo.readObject(input, clazz);
    }
    
    public static <T> T readObjectFromString(String str, Class<T> clazz) {
        try {
            return readObjectFromByteArray(Base64.decodeBase64(str.getBytes(DEFAULT_ENCODING)), clazz);
        } catch (UnsupportedEncodingException e) {
            throw new IllegalStateException(e);
        }
    }
}

Hessian

package cuit.pymjl.utils;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;


public class HessianUtils {
    
    public static byte[] serialize(Object obj) {
        Hessian2Output ho = null;
        ByteArrayOutputStream baos = null;
        try {
            baos = new ByteArrayOutputStream();
            ho = new Hessian2Output(baos);
            ho.writeObject(obj);
            ho.flush();
            return baos.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
            throw new RuntimeException("serialize failed");
        } finally {
            if (null != ho) {
                try {
                    ho.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != baos) {
                try {
                    baos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public static  <T> T deserialize(byte[] bytes, Class<T> clazz) {
        Hessian2Input hi = null;
        ByteArrayInputStream bais = null;
        try {
            bais = new ByteArrayInputStream(bytes);
            hi = new Hessian2Input(bais);
            Object o = hi.readObject();
            return clazz.cast(o);
        } catch (Exception ex) {
            throw new RuntimeException("deserialize failed");
        } finally {
            if (null != hi) {
                try {
                    hi.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != bais) {
                try {
                    bais.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Protostuff

package cuit.pymjl.utils;
import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ProtostuffUtils {
    
    private static final LinkedBuffer BUFFER = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
    
    private static final Map<Class<?>, Schema<?>> SCHEMA_CACHE = new ConcurrentHashMap<>();
​
    
    @SuppressWarnings("unchecked")
    public static <T> byte[] serialize(T obj) {
        Class<T> clazz = (Class<T>) obj.getClass();
        Schema<T> schema = getSchema(clazz);
        byte[] data;
        try {
            data = ProtostuffIOUtil.toByteArray(obj, schema, BUFFER);
        } finally {
            BUFFER.clear();
        }
        return data;
    }
​
    
    public static <T> T deserialize(byte[] data, Class<T> clazz) {
        Schema<T> schema = getSchema(clazz);
        T obj = schema.newMessage();
        ProtostuffIOUtil.mergeFrom(data, obj, schema);
        return obj;
    }
    @SuppressWarnings("unchecked")
    private static <T> Schema<T> getSchema(Class<T> clazz) {
        Schema<T> schema = (Schema<T>) SCHEMA_CACHE.get(clazz);
        if (schema == null) {
            schema = RuntimeSchema.getSchema(clazz);
            if (schema == null) {
                SCHEMA_CACHE.put(clazz, schema);
            }
        }
        return schema;
    }
}

创建一个实体类进行测试:

package cuit.pymjl.entity;
​
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serial;
import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student implements Serializable {
    @Serial
    private static final long serialVersionUID = -91809837793898L;
​
    private String name;
    private String password;
    private int age;
    private String address;
    private String phone;
}

序列化后字节所占空间大小比较

编写测试类:

public class MainTest {
    @Test
    void testLength() {
        Student student = new Student("pymjl", "123456", 18, "北京", "123456789");
        int kryoLength = KryoUtils.writeObjectToByteArray(student).length;
        int hessianLength = HessianUtils.serialize(student).length;
        int protostuffLength = ProtostuffUtils.serialize(student).length;
        System.out.println("kryoLength: " + kryoLength);
        System.out.println("hessianLength: " + hessianLength);
        System.out.println("protostuffLength: " + protostuffLength);
    }
}

运行截图:

image-20220702131907826

由图可知,Hessian序列化后字节所占的空间都显著比其他两种方式要大得多

其他比较

  • Hessian使用固定长度存储int和long,而kryo使用变长的int和long保证这种基本数据类型序列化后尽量小,实际应用中,很大的数据不会经常出现。
  • Kryo进行序列化的时候,需要传入完整类名或者利用 register() 提前将类注册到Kryo上,其类与一个int型的ID相关联,序列中只存放这个ID,因此序列体积就更小,而Hessian则是将所有类字段信息都放入序列化字节数组中,直接利用字节数组进行反序列化,不需要其他参与,因为存的东西多处理速度就会慢点
  • Kryo使用不需要实现Serializable接口,Hessian则需实现
  • Kryo数据类的字段增、减,序列化和反序列化时无法兼容,而Hessian则兼容,Protostuff是只能在末尾添加新字段才兼容
  • Kryo和Hessian使用涉及到的数据类中必须拥有无参构造函数
  • Hessian会把复杂对象的所有属性存储在一个Map中进行序列化。所以在父类、子类存在同名成员变量的情况下,Hessian序列化时,先序列化子类,然后序列化父类,因此反序列化结果会导致子类同名成员变量被父类的值覆盖
  • Kryo不是线程安全的,要通过ThreadLocal或者创建Kryo线程池来保证线程安全,而Protostuff则是线程安全的
  • Protostuff和Kryo序列化的格式有相似之处,都是利用一个标记来记录字段类型,因此序列化出来体积都比较小

小结

 优点缺点
Kryo速度快,序列化后体积小跨语言支持较复杂
Hessian默认支持跨语言较慢
Protostuff速度快,基于protobuf需静态编译
Protostuff-Runtime无需静态编译,但序列化前需预先传入schema不支持无默认构造函数的类,反序列化时需用户自己初始化序列化后的对象,其只负责将该对象进行赋值
Java使用方便,可序列化所有类速度慢,占空间

到此这篇关于Java Kryo,Protostuff,Hessian序列化方式对比的文章就介绍到这了,更多相关Java序列化内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Java Kryo,Protostuff,Hessian序列化方式对比

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Java Kryo,Protostuff,Hessian序列化方式是什么

本篇内容主要讲解“Java Kryo,Protostuff,Hessian序列化方式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java Kryo,Protostuff,Hessian序
2023-07-02

java原生序列化和Kryo序列化性能实例对比分析

简介最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括:专门针对Java语言的:Kryo,FST等等跨语言的:Protostuff,ProtoBuf,Thrift,Avro,MsgPack等等这些序列化方式的性
2023-05-31

java序列化方式有哪些

Java序列化方式有以下几种:1. Java默认的序列化:通过实现Serializable接口来实现,默认使用ObjectOutputStream和ObjectInputStream进行对象的序列化和反序列化。2. 自定义序列化:通过实现E
2023-08-15

java序列化的方式有哪些

Java序列化的方式有:1. 实现Serializable接口:在需要序列化的类中实现Serializable接口,该接口没有任何方法,只是起到一个标记作用,表示该类可以被序列化。2. 实现Externalizable接口:与Seriali
2023-09-27

Java对象被序列化方法有哪些

本篇内容主要讲解“Java对象被序列化方法有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java对象被序列化方法有哪些”吧!Java对象为什么需要被序列化序列化能够将对象转为二进制流,对象
2023-06-20

java对象序列化的方法有哪些

Java对象序列化的方法有以下几种:1. 实现Serializable接口:该接口是Java提供的序列化接口,只需要让类实现该接口即可实现序列化。这是最常用的方法。```javapublic class MyClass implements
2023-08-24

Java反序列化对象的方法是什么

Java中反序列化对象的方法是使用ObjectInputStream类的readObject()方法。可以使用以下步骤来反序列化一个对象:1. 创建一个FileInputStream对象,用于读取存有序列化对象的文件。2. 创建一个Obje
2023-09-14

如何将Java对象序列化成JSON和XML格式

这篇文章给大家分享的是有关如何将Java对象序列化成JSON和XML格式的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1.先定义一个Java对象Person:public class Person{ String
2023-05-30

SpringBoot整合Redis实现序列化存储Java对象的操作方法

目录一、背景1、思考2、方案二、源码分析三、注入RedisTemplate1、引入依赖2、Redis 连接信息3、Redis 核心配置类4、Redis工具类四、测试1、创建 Java 实体类 UserInfo2、测试用例3、测试结果之前介绍
2023-03-23

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录