kryo是一个高性能的序列化/反序列化工具,由于其变长存储特性并使用了字节码生成机制,拥有较高的运行速度和较小的体积。
引入maven依赖
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo</artifactId>
<version>4.0.2</version>
</dependency>
需要注意的是,由于kryo使用了较高版本的asm,可能会与业务现有依赖的asm产生冲突,这是一个比较常见的问题。只需将依赖改成:
<dependency>
<groupId>com.esotericsoftware</groupId>
<artifactId>kryo-shaded</artifactId>
<version>4.0.2</version>
</dependency>
这算是kryo的一个特点,可以把对象信息直接写到序列化数据里,反序列化的时候可以精确地找到原始类信息,不会出错,这意味着在写readxxx方法时,无需传入Class或Type类信息。
相应的,kryo提供两种读写方式。记录类型信息的writeClassAndObject/readClassAndObject方法,以及传统的writeObject/readObject方法。
kryo的对象本身不是线程安全的,所以我们有两种选择来保障线程安全。
使用Threadlocal来保障线程安全:
private static final ThreadLocal<Kryo> kryoLocal = new ThreadLocal<Kryo>() {
protected Kryo initialValue() {
Kryo kryo = new Kryo();
kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(
new StdInstantiatorStrategy()));
return kryo;
};
};
或者使用kryo提供的pool:
public KryoPool newKryoPool() {
return new KryoPool.Builder(() -> {
final Kryo kryo = new Kryo();
kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(
new StdInstantiatorStrategy()));
return kryo;
}).softReferences().build();
}
在上面注意到kryo.setInstantiatorStrategy(new Kryo.DefaultInstantiatorStrategy(new StdInstantiatorStrategy()));
这句话显示指定了实例化器。
在一些依赖了kryo的开源软件中,可能由于实例化器指定的问题而抛出空指针异常。例如hive的某些版本中,默认指定了StdInstantiatorStrategy。
public static ThreadLocal<Kryo> runtimeSerializationKryo = new ThreadLocal<Kryo>() {
@Override
protected synchronized Kryo initialValue() {
Kryo kryo = new Kryo();
kryo.setClassLoader(Thread.currentThread().getContextClassLoader());
kryo.register(java.sql.Date.class, new SqlDateSerializer());
kryo.register(java.sql.Timestamp.class, new TimestampSerializer());
kryo.register(Path.class, new PathSerializer());
kryo.setInstantiatorStrategy(new StdInstantiatorStrategy());
......
return kryo;
};
};
而StdInstantiatorStrategy在是依据JVM version信息及JVM vendor信息创建对象的,可以不调用对象的任何构造方法创建对象。
那么例如碰到ArrayList这样的对象时候,就会出问题。观察一下ArrayList的源码:
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
既然没有调用构造器,那么这里elementData会是NULL,那么在调用类似ensureCapacity方法时,就会抛出一个异常。
public void ensureCapacity(int minCapacity) {
if (minCapacity > elementData.length
&& !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
&& minCapacity <= DEFAULT_CAPACITY)) {
modCount++;
grow(minCapacity);
}
}
解决方案很简单,就如框架中代码写的一样,显示指定实例化器,首先使用默认无参构造策略DefaultInstantiatorStrategy,若创建对象失败再采用StdInstantiatorStrategy。
当kryo写一个对象的实例的时候,默认需要将类的完全限定名称写入。将类名一同写入序列化数据中是比较低效的,所以kryo支持通过类注册进行优化。
kryo.register(SomeClassA.class);
kryo.register(SomeClassB.class);
kryo.register(SomeClassC.class);
注册会给每一个class一个int类型的Id相关联,这显然比类名称高效,但同时要求反序列化的时候的Id必须与序列化过程中一致。这意味着注册的顺序非常重要。
但是由于现实原因,同样的代码,同样的Class在不同的机器上注册编号任然不能保证一致,所以多机器部署时候反序列化可能会出现问题。
所以kryo默认会禁止类注册,当然如果想要打开这个属性,可以通过kryo.setRegistrationRequired(true);
打开。
这是对循环引用的支持,可以有效防止栈内存溢出,kryo默认会打开这个属性。当你确定不会有循环引用发生的时候,可以通过kryo.setReferences(false);
关闭循环引用检测,从而提高一些性能。
kryo对int和long类型都采用了可变长存储的机制,以int为例,一般需要4个字节去存储,而对kryo来说,可以通过1-5个变长字节去存储,从而避免高位都是0的浪费。
最多需要5个字节存储是因为,在变长存储int过程中,一个字节的8位用来存储有效数字的只有7位,最高位用于标记是否还需读取下一个字节,1表示需要,0表示不需要。
在对string的存储中也有变长存储的应用,string序列化的整体结构为length+内容,那么length也会使用变长int写入字符的长度。
在实际开发中,class增删字段是很常见的事情,但对于kryo来说,确是不支持的,而如果恰好需要使用缓存,那么这个问题会被放得更大。
例如一个对象使用kryo序列化后,数据放入了缓存中,而这时候如果这个对象增删了一个属性,那么缓存中反序列化的时候就会报错。所以频繁使用缓存的场景,可以尽量避免kryo。
不过现在的Kryo提供了兼容性的支持,使用CompatibleFieldSerializer.class,在kryo.writeClassAndObject时候写入的信息如下:
class name|field length|field1 name|field2 name|field1 value| filed2 value
而在读入kryo.readClassAndObject时,会先读入field names,然后匹配当前反序列化类的field和顺序再构造结果。
当然如果在做好缓存隔离的情况下,这一切都不用在意。
Original url: Access
Created at: 2019-04-23 15:44:57
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
java windows火焰图_mob64ca12ec8020的技术博客_51CTO博客 - 在windows下不可行,不知道作者是怎样搞的 监听SpringBoot 服务启动成功事件并打印信息_监听springboot启动完毕-CSDN博客 SpringBoot中就绪探针和存活探针_management.endpoint.health.probes.enabled-CSDN博客 u2u转换板 - 嘉立创EDA开源硬件平台 Spring Boot 项目的轻量级 HTTP 客户端 retrofit 框架,快来试试它!_Java精选-CSDN博客 手把手教你打造一套最牛的知识笔记管理系统! - 知乎 - 想法有重合-理论可参考 安宇雨 闲鱼 机械键盘 客制化 开贴记录 文本 linux 使用find命令查找包含某字符串的文件_beijihukk的博客-CSDN博客_find 查找字符串 ---- mac 也适用 安宇雨 打字音 记录集合 B站 bilibili 自行搭建 开坑 真正的客制化 安宇雨 黑苹果开坑 查找工具包maven pom 引用地 工具网站 Dantelis 介绍的玩轴入坑攻略 --- 关于轴的一些说法 --- 非官方 ---- 心得而已 --- 长期开坑更新 [本人问题][新开坑位]关于自动化测试的工具与平台应用 机械键盘 开团 网站记录 -- 能做一个收集的程序就好了 不过现在没时间 -- 信息大多是在群里发的 - 你要让垃圾佬 都去一个地方看难度也是很大的 精神支柱 [超级前台]sprinbboot maven superdesk-app 记录 [信息有用] [环境准备] [基本完成] [sebp/elk] 给已创建的Docker容器增加新的端口映射 - qq_30599553的博客 - CSDN博客 [正在研究] Elasticsearch, Logstash, Kibana (ELK) Docker image documentation elasticsearch centos 安装记录 及 启动手记 正式服务器 39 elasticsearch 问题合集 不断更新 6.1.1 | 6.5.1 两个版本 博客程序 - 测试 - bug记录 等等问题 laravel的启动过程解析 - lpfuture - 博客园 OAuth2 Server PHP 用 Laravel 搭建带 OAuth2 验证的 RESTful 服务 | Laravel China 社区 - 高品质的 Laravel 和 PHP 开发者社区 利用Laravel 搭建oauth2 API接口 附 Unauthenticated 解决办法 - 煮茶的博客 - SegmentFault 思否 使用 OAuth2-Server-php 搭建 OAuth2 Server - 午时的海 - 博客园 基于PHP构建OAuth 2.0 服务端 认证平台 - Endv - 博客园 Laravel 的 Artisan 命令行工具 Laravel 的文件系统和云存储功能集成 浅谈Chromium中的设计模式--终--Observer模式 浅谈Chromium中的设计模式--二--pre/post和Delegate模式 浅谈Chromium中的设计模式--一--Chromium中模块分层和进程模型 DeepMind 4 Hacking Yourself README.md update 20211011
Laravel China 简书 知乎 博客园 CSDN博客 开源中国 Go Further Ryan是菜鸟 | LNMP技术栈笔记 云栖社区-阿里云 Netflix技术博客 Techie Delight Linkedin技术博客 Dropbox技术博客 Facebook技术博客 淘宝中间件团队 美团技术博客 360技术博客 古巷博客 - 一个专注于分享的不正常博客 软件测试知识传播 - 测试窝 有赞技术团队 阮一峰 语雀 静觅丨崔庆才的个人博客 软件测试从业者综合能力提升 - isTester IBM Java 开发 使用开放 Java 生态系统开发现代应用程序 pengdai 一个强大的博主 HTML5资源教程 | 分享HTML5开发资源和开发教程 蘑菇博客 - 专注于技术分享的博客平台 个人博客-leapMie 流星007 CSDN博客 - 舍其小伙伴 稀土掘金 Go 技术论坛 | Golang / Go 语言中国知识社区
最新评论