概述

Kryo 是一个快速序列化/反序列化工具,依赖于字节码生成机制(底层使用了 ASM 库),因此在序列化速度上有一定的优势,但正因如此,其使用也只能限制在基于 JVM 的语言上。

和 Hessian 类似,Kryo 序列化出的结果,是其自定义的、独有的一种格式。由于其序列化出的结果是二进制的,也即 byte[],因此像 Redis 这样可以存储二进制数据的存储引擎是可以直接将 Kryo 序列化出来的数据存进去。当然你也可以选择转换成 String 的形式存储在其他存储引擎中(性能有损耗)。

基础用法

介绍了这么多,接下来我们就来看看 Kryo 的基础用法吧。其实对于序列化框架来说,API 基本都差不多,毕竟入参和出参通常都是确定的(需要序列化的对象/序列化的结果)。在使用 Kryo 之前,我们需要引入相应的依赖。

<dependency><groupId>com.esotericsoftware</groupId><artifactId>kryo</artifactId><version>5.2.0</version>
</dependency>
复制代码

基本使用如下所示

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import java.io.*;public class HelloKryo {static public void main(String[] args) throws Exception {Kryo kryo = new Kryo();kryo.register(SomeClass.class);SomeClass object = new SomeClass();object.value = "Hello Kryo!";Output output = new Output(new FileOutputStream("file.bin"));kryo.writeObject(output, object);output.close();Input input = new Input(new FileInputStream("file.bin"));SomeClass object2 = kryo.readObject(input, SomeClass.class);input.close();System.out.println(object2.value);}static public class SomeClass {String value;}
}
复制代码

Kryo 类会自动执行序列化。Output 类和 Input 类负责处理缓冲字节,并写入到流中。

如果序列化前和序列化后类的字段不一致,反序列化会失败。

Kryo 的序列化

作为一个灵活的序列化框架,Kryo 并不关心读写的数据,作为开发者,你可以随意使用 Kryo 提供的那些开箱即用的序列化器。

Kryo 的注册

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

kryo.register(SomeClass.class);
复制代码

或者

kryo.register(SomeClass.class, 1);
复制代码

可以明确指定注册类的 int ID,但是该 ID 必须大于等于 0。如果不提供,内部将会使用 int++的方式维护一个有序的 int ID 生成。

Kryo 的序列化器

Kryo 支持多种序列化器,通过源码我们可窥知一二

具体可参考 :point_right: 「Kryo 支持的序列化类型」

虽然 Kryo 提供的序列化器可以读写大多数对象,但开发者也可以轻松的制定自己的序列化器。篇幅限制,这里就不展开说明了,仅以默认的序列化器为例。

对象引用

在新版本的 Kryo 中,默认情况下是不启用对象引用的。这意味着如果一个对象多次出现在一个对象图中,它将被多次写入,并将被反序列化为多个不同的对象。

举个例子,当开启了引用属性,每个对象第一次出现在对象图中,会在记录时写入一个 varint,用于标记。当此后有同一对象出现时,只会记录一个 varint,以此达到节省空间的目标。此举虽然会节省序列化空间,但是是一种用时间换空间的做法,会影响序列化的性能,这是因为在写入/读取对象时都需要进行追踪。

开发者可以使用 kryo 自带的 setReferences 方法来决定是否启用 Kryo 的引用功能。

public class KryoReferenceDemo {public static void main(String[] args) throws FileNotFoundException {Kryo kryo = new Kryo();kryo.register(User.class);kryo.register(Account.class);User user = new User();user.setUsername("alvin");Account account = new Account();account.setAccountNo("10000");// 循环引用user.setAccount(account);account.setUser(user);// 这里需要设置为true,才不会报错kryo.setReferences(true);Output output = new Output(new FileOutputStream("kryoreference.bin"));kryo.writeObject(output, user);output.close();Input input = new Input(new FileInputStream("kryoreference.bin"));User object2 = kryo.readObject(input, User.class);input.close();System.out.println(object2.getUsername());System.out.println(object2.getAccount().getAccountNo());}public static class User {private String username;private Account account;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public Account getAccount() {return account;}public void setAccount(Account account) {this.account = account;}}public static class Account {private String accountNo;private String amount;private User user;public String getAccountNo() {return accountNo;}public void setAccountNo(String accountNo) {this.accountNo = accountNo;}public String getAmount() {return amount;}public void setAmount(String amount) {this.amount = amount;}public User getUser() {return user;}public void setUser(User user) {this.user = user;}}
}
复制代码

如果序列化前的setReferences(false), 后面设置setReferences(true)进行反序列化,会失败。

线程不安全

Kryo 不是线程安全的。每个线程都应该有自己的 Kryo 对象、输入和输出实例。

因此在多线程环境中,可以考虑使用 ThreadLocal 或者对象池来保证线程安全性。

ThreadLocal + Kryo 解决线程不安全

ThreadLocal 是一种典型的牺牲空间来换取并发安全的方式,它会为每个线程都单独创建本线程专用的 kryo 对象。对于每条线程的每个 kryo 对象来说,都是顺序执行的,因此天然避免了并发安全问题。创建方法如下:

static private final ThreadLocal<Kryo> kryos = new ThreadLocal<Kryo>() {protected Kryo initialValue() {Kryo kryo = new Kryo();// 在此处配置kryo对象的使用示例,如循环引用等return kryo;};
};Kryo kryo = kryos.get();
复制代码

之后,仅需要通过 kryos.get() 方法从线程上下文中取出对象即可使用。

对象池 + Kryo 解决线程不安全

「池」是一种非常重要的编程思想,连接池、线程池、对象池等都是

「复用」思想的体现,通过将创建的“对象”保存在某一个“容器”中,以便后续反复使用,避免创建、销毁的产生的性能损耗,以此达到提升整体性能的作用。

Kryo 对象池原理也是如此。Kryo 框架自带了对象池的实现,整个使用过程不外乎创建池、从池中获取对象、归还对象三步,以下为代码实例。

// Pool constructor arguments: thread safe, soft references, maximum capacity
Pool<Kryo> kryoPool = new Pool<Kryo>(true, false, 8) {protected Kryo create () {Kryo kryo = new Kryo();// Kryo 配置return kryo;}
};// 获取池中的Kryo对象
Kryo kryo = kryoPool.obtain();
// 将kryo对象归还到池中
kryoPool.free(kryo);
复制代码

创建 Kryo 池时需要传入三个参数,其中第一个参数用于指定是否在 Pool 内部使用同步,如果指定为 true,则允许被多个线程并发访问。第三个参数适用于指定对象池的大小的,这两个参数较容易理解,因此重点来说一下第二个参数。

如果将第二个参数设置为 true,Kryo 池将会使用 java.lang.ref.SoftReference 来存储对象。这允许池中的对象在 JVM 的内存压力大时被垃圾回收。Pool clean 会删除所有对象已经被垃圾回收的软引用。当没有设置最大容量时,这可以减少池的大小。当池子有最大容量时,没有必要调用 clean,因为如果达到了最大容量,Pool free 会尝试删除一个空引用。

创建完 Kryo 池后,使用 kryo 就变得异常简单了,只需调用 kryoPool.obtain() 方法即可,使用完毕后再调用 kryoPool.free(kryo) 归还对象,就完成了一次完整的租赁使用。

理论上,只要对象池大小评估得当,就能在占用极小内存空间的情况下完美解决并发安全问题。如果想要封装一个 Kryo 的序列化方法,可以参考如下的代码

public static byte[] serialize(Object obj) {Kryo kryo = kryoPool.obtain();// 使用 Output 对象池会导致序列化重复的错误(getBuffer返回了Output对象的buffer引用)try (Output opt = new Output(1024, -1)) {kryo.writeClassAndObject(opt, obj);opt.flush();return opt.getBuffer();}finally {kryoPool.free(kryo);}
}
复制代码

小结

相较于 JDK 自带的序列化方式,Kryo 的性能更快,并且由于 Kryo 允许多引用和循环引用,在存储开销上也更小。

只不过,虽然 Kryo 拥有非常好的性能,但其自身却舍去了很多特性,例如线程安全、对序列化对象的字段修改等。虽然这些弊端可以通过 Kryo 良好的扩展性得到一定的满足,但是对于开发者来说仍然具有一定的上手难度,不过这并不能影响其在 Java 中的地位。

Java高性能序列化工具Kryo序列化相关推荐

  1. krait和kryo_java原生序列化和Kryo序列化性能实例对比分析

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

  2. 解决Python自带的json序列化工具不能序列化datetime类型数据问题

    解决Python自带的json序列化工具不能序列化datetime类型数据问题 参考文章: (1)解决Python自带的json序列化工具不能序列化datetime类型数据问题 (2)https:// ...

  3. avro和java原生序列化的区别,java原生序列化和Kryo序列化性能比较

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

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

    1.美图 2.概述 最近几年,各种新的高效序列化方式层出不穷,不断刷新序列化性能的上限,最典型的包括: 专门针对Java语言的:Kryo,FST等等 跨语言的:Protostuff,ProtoBuf, ...

  5. krait和kryo_java原生序列化和Kryo序列化性能比较

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

  6. Java序列化工具的对比

    在Java开发工具当中,Java序列化工具是比较常用的,而且种类也有不少,下面小编就来做个对比: 1.Java序列化工具的技术原理对比 Binary Formats & language-sp ...

  7. spark之kryo序列化及其使用

    spark之kryo序列化 spark之kryo 序列化 Spark 中使用 Kryo序列化 中文切词案例: spark之kryo 序列化 1.定义:把对象转换为字节序列的过程称为对象的序列化. 把字 ...

  8. spark之kryo 序列化

    几乎所有的资料都显示kryo 序列化方式优于java自带的序列化方式,而且在spark2.*版本中都是默认采用kryo 序列化.因此本文将做kryo 做一个测试以验证其性能. 1.先给出定义:     ...

  9. java get方法不序列化_java常用序列化与反序列化方法

    序列化工具类 序列化即将对象序列化为字节数组,反序列化就是将字节数组恢复成对象. 主要的目的是方便传输和存储. 序列化工具类: public class SerializeUtil { private ...

  10. java 序列化工具kryo_java jackson avro kryo等几种序列化与反序列化工具的使用

    最近由于工作需要,需要研究常用的集中序列化方式,主要目的是对象序列化后占用空间会大大减少,便于存储和传输,下面是几种序列化方式的使用demo 1. Java自带的Serialize 依赖jar包:无 ...

最新文章

  1. 14 个 Spring MVC 顶级技巧,随时用随时爽,一直用一直爽
  2. EF6学习笔记五:继承三策略
  3. 【c++】28.虚析构函数、纯虚函数
  4. 灰度共生矩阵及其数字特征_数字系统及其表示
  5. 九度OJ 1059:abc
  6. Python socket TCP
  7. Java中什么是JAP之hibernate-mvc修改功能-Springmvc
  8. 用Prime95来做linux下CPU压力测试
  9. ## python爬取MM131整站图片到本地
  10. Alex 的 Hadoop 菜鸟教程: 第17课 更快速的MapReduce - Spark
  11. 浅谈IPFS新激励层Filenet究竟是什么,令牌、公链、应用?超级云系统!
  12. 基于ISO7637的车载电源系统设计
  13. 通信原理ami码c语言实现,通信原理AMI码型变换实验
  14. 用视频录制软件Captura学习网课
  15. 文末彩蛋 | 这个 Request URL 长得好不一样
  16. 51单片机STC89C52控制LED跑马灯左移(使用C51自带库_crol_()实现),详细注释
  17. Oracle Client卸载
  18. indesign怎么查找拼音_用Indesign CS 巧排拼音的方法
  19. numpy随机索引(不重复)和打乱元素
  20. 计算机复试中的一些问题

热门文章

  1. DIY兼容机装苹果系统
  2. vue自定义指令—directive详解
  3. 利用wordcloud和jieba制作词云
  4. 自动驾驶词汇概念介绍
  5. 计算机硬件开关打开无线网络适配器,ibm笔记本电脑无线硬件开启步骤
  6. pip换源工具pqi
  7. 2021-05-11PLC通信Libnodave-明天继续
  8. Vue3 Suspense 组件
  9. 脉冲计数器校准配置方案
  10. NLP 语义匹配:经典前沿方案整理