[Java] 序列化(Serialization)的本质是什么?在Java中怎么实现?为什么要了解序列化技术?序列化技术选型要点是什么?

news2024/11/25 10:54:26

文章目录

  • 前言
  • 序列化是什么?
    • 理解对象在内存中是如何存储的
    • 数据在进程内存中的分布图
    • 数据被序列化之后在内存中的分布图
  • 序列化/反序列化的本质?
  • 序列化在Java中的实现?
    • 1. JDK Serialization(不推荐使用)
    • 2. 第三方实现:Kryo
    • 3. 第三方实现:ProtoStuff 和 Protobuf
  • 为什么要了解序列化技术?
  • 序列化技术选型要点
  • 补充:byte[] vs byte stream
  • 结语

前言


序列化是相对比较重要也比较简单,但也比较容易被忽视的一类基础性知识。序列化在网络应用特别是如今的分布式系统中被大量使用,理解好序列化是理解好网络应用以及分布式系统架构的基础。笔者将通过本文去介绍序列化是什么、以及Java中的实现及选型的技术要点。

序列化是什么?


序列化,英:Serialization。序列化的定义非常简单,维基百科定义截图如下:

维基百科 - 序列化的定义

用白话来讲就是:应用进程中的数据(数据结构或对象) 里的信息给转换成 可以进行IO的格式
进一步解释的话:

  1. 应用进程中的数据(数据结构或对象):在应用内存中数据通常是以散乱分布加指针的方式组织起来的。
  2. 可以进行IO的格式:这个一般我们认为是字节数组(byte[])。

序列化就是把在内存中散乱分布的信息(数据),把它们收集起来转换成一个byte数组来表示,并且这个过程是可逆的。那么这个数据收集和转换的过程就被称为序列化,逆向处理呢,则被称为反序列化(deserialization)

这么说可能依然难以理解,笔者会在接下来的几个章节中提供图例供大家参考。

理解对象在内存中是如何存储的

对于大部分OOP语言来说,其内部的可以说所有 实例都由两部分(对象头和对象体) 组成,它们在内存中是紧密连续排列的。对象体中原生数据类型(primitive data type)不是指针,意味着在对象的内存中能直接看到对应的原生数据类型的数据,引用类型(reference data type)则是表现为一个指针(根据jvm的具体情况可以使4字节或8字节来存储),其包含的信息是另一个对象的内存地址

也正是因为指针,让我们的数据其实在内存中是分布在多处被存储起来的,所以需要序列化技术把这些分散的数据集中起来,用 连续的字节(byte[]) 表示

当然笔者上面提到的知识点都是非常常规的理解,在Java里对象存储的时候还会有属性对齐(alignment)这种处理,本文不做详细讨论,如果有兴趣的读者可以去阅读这篇文章:《Memory Layout of Objects in Java》,这篇文章中的jol-core库在笔者验证java内置锁的几种状态时帮了大忙,十分推荐去看其Sample代码。

数据在进程内存中的分布图

在上一节我们解释了Java对象在内存中的构造,本章我们用代码和图文的去理解一下数据究竟是如何在进程内存中分布的。假设我们有这样的一个UserInfo类,包含username和uid两个属性。

UserInfo类
然后呢我们有这个类的实例如下:姓名张三,id是1。

UserInfo userInfo = new UserInfo("张三", 1);

那么这个实例在内存中则是长这样:

UserInfo 张三实例
红框里,第一个Object Header的地方代表着张三这个UserInfo实例,第二个Object Header的地方代表着"张三"这个String实例。不难看出一个对象实例若是有引用类型,其数据通常是分散存储在堆内存的各个地方。物理上就是分散的,不是连续的。

数据被序列化之后在内存中的分布图

上一节我们用图和代码的方式介绍了,对象数据在堆内存中的分布。这一节我们去看一下序列化处理后的数据在内存中长什么样。参考下图:

序列化后的数据在内存中的分布情况

红框里呢,就是我们序列化处理之后的结果了,简单来说对象序列化后的结果是一个byte[],也就是字节数组,Java中数组也是一个对象实例。所以你能在上图中看到Object Header,后面紧跟一串byte,代表着序列化后的字节数据。此时,因为我们对象的所有信息都被集中起来了,所以可以把这些数据通过文件IO持久化到本地文件中,也可以通过网路IO发送至远端服务器上的进程里去处理

相信到这里,基本上我们都能很清晰的认识到序列化到底是在干什么了。那么反序列化其实也同理,只不过这个过程的输出输入和序列化是反着的而已。

序列化/反序列化的本质?


上一章节我们介绍了数据的序列化反序列化的本质就是一个数据转换(Mapping)的一个过程,那么不计成本地实现起来其实就很简单了。

序列化反序列化

只要你能有一套方案(算法或处理数据的方式)能够让对象实例和byte[]实现相互转换,那么这其实就是一种序列化的实现方式。只不过和JVM、哈希方法一样,可以有众多不同公司、组织的实现,不同实现的性能和特性也是有区别的,这个我们放到后面章节:《序列化技术选型要点》里讲。

就比如你甚至可以这么实现:

  1. 序列化:对象实例 ⇒ JSON对象 ⇒ JSON字符串 ⇒ byte[]
  2. 反序列化:byte[] ⇒ JSON字符串 ⇒ JSON对象 ⇒ 对象实例

虽然这种实现性能会很低,但是不可否认的是这种做法也是一种序列化和反序列化的实现。笔者仅希望大家理解序列化/反序列化的本质就是数据互相转换的一个过程。

序列化在Java中的实现?


Java中,有很多库提供了序列化的实现。本章就简单列举一下,在Java中实现序列化反序列化的一些技术。

本章并会讨论所有的序列化实现,笔者作为应用程序的开发者,很难全面了解市面上所有的序列化技术。本章节仅供参考!如果需要技术选型决策时,最好去参考相应的benchmark文章或是自家研发部根据实际需求去做调研之后再做选择!

1. JDK Serialization(不推荐使用)

比较经典的、不被看好的序列化技术,JDK自带的序列化技术。开发者可以实现 java.io.Serializable 接口。
然后用 java.io.ObjectOutputStream 类实现序列化,以及 java.io.ObjectInputStream 类实现反序列化

JDK自带的序列化技术,有很多缺点所以是 不被推荐使用的。就比如性能问题、序列化后的数据很占用空间、以及严重的反序列化安全漏洞。这个在笔者持有的《Effective Java Third Edition》书中《12. Serialization Item 85: Prefer alternatives to Java Serialization》中有详细的描述。简单来说就是当Java程序对特定的JDK序列化后的数据做反序列化时会有超过太阳生命周期的运算时间,这个漏洞可以用来攻击服务器让服务器的处理线程瘫痪以及占用大量的计算资源。有兴趣的读者可以自行去笔者提到的章节里去看。

总之,JDK序列化是非常非常不推荐使用的序列化技术。

2. 第三方实现:Kryo

Kryo是一个开源的高性能的Java序列化/反序列化的库。github传送门。Kryo库的效率很高且作为序列化结果的byte[]占用空间也较小,因此在诸多顶级项目里都有被使用。

使用到Kryo的项目

咱们国内比较热门的dubbo也悄悄咪咪列在倒数第三位。而在dubbo的文档(下图)中也是提到,如果没有跨语言序列化的需求,那么推荐使用Kryo去做Java 2 Java的序列化。

dubbo关于序列化的文档

3. 第三方实现:ProtoStuff 和 Protobuf

出自谷歌的Protobuf 和基于Protobuf的ProtoStuff,笔者对这两个的了解并不多,在看java序列化库benchmark时有注意到这个库的性能也是非常的高,并且是跨语言的。

Protobuf支持的语言

所谓跨语言就是你用Java编写的进程,数据序列化之后能够在别的如Python、PHP、C#等语言编写的进程内反序列化,就是所谓的跨语言。上面的Kryo就是针对Java 2 Java,不支持跨语言。而Protobuf和ProtoStuff则是支持跨语言的。我们的Protobuf支持的语言呢在上面截图里面也有提到。

Protobuf在Github上的Star数量较高,但也比较难去使用。需要自己去写.proto文件去定义消息,然后生成对应支持的语言的代码文件。相关文档链接。

Protobuf 文档

Protostuff则是再Protobuf的基础上,对Java语言做了特化的,用Maven导入即可,相对于Java开发者来说可能会比较友好一点,不需要自己编写.proto文件然后生成代码。也因为特化,Star数量就少了很多,但是使用起来就简单很多。

Protostuff Sample

为什么要了解序列化技术?


每一种技术都有其存在的意义,也就是其所对应的需求(needs)。序列化技术自然也不例外,如果你是CRUD程序员,那么基本上除了业务流程处理之外,你其实并不太可能会直接用到序列化技术。而如果你是通用组件的开发者,比如私有通信协议的开发者、rpc框架的开发者,总之一切涉及到进程间通信的开发人员,都不可避免接触到序列化技术。

亦或是你项目中用到了Redis,配置Redis也需要有序列化相关的知识。

配置RedisTemplate

总之,在互联网领域,分布式架构之下的企业级系统呢,因为大量涉及到多个服务器上的多个进程的通信,理解序列化技术是理解分布式系统的基础

序列化技术选型要点


在选择应用哪种序列化技术时,也有一些基本的考量点。通常呢可以有这么几点:

  1. 性能:需要考虑在业务的性能需求,不用说就知道库的性能越高越好。
  2. 安全性:亦或是叫脆弱性(vulnerability),对于安全性的需求。比如前面提到的JDK 序列化有卡死线程的漏洞。
  3. 大小:序列化结果所占用的空间大小,序列化后的字节数据通常会持久化到硬盘(占存储资源)或走网络传输给其他服务器(占带宽资源),这个指标自然是越低越好。

除此之外还有如:

  1. 跨语言:企业内部系统是否有跨语言通信的需求。
  2. 可维护性:人力资源市场上,这种技术的流行程度。越流行的技术可维护性越高,维护成本越低。

补充:byte[] vs byte stream


对于序列化的结果呢,笔者本文主要使用的字节数组作为结果,那么其实并不是说结果一定是字节数组,也可以是字节流。它俩本身包含的信息是一样的(都是一串连续的字节数据),通常情况下如果数据过大,需要通过分批次处理呢,我们会选用流式的处理方式,反之如果数据不大,用byte数组也不是不可以

我们只需要知道它们所携带的信息量,本质是一样的 即可。

结语


序列化技术本身与业务代码关系并不大,反而更多与企业系统架构关系更大一点。越是趋向于分布式架构的系统,对于好的序列化技术的需求越大,而业务压力小甚至不需要做分布式架构的系统则对于序列化技术的需求没有那么大。目前来看,大部分企业都在往分布式架构系统靠的大环境下,理解序列化技术的本质也变得越发重要。

我是虎猫,希望本文对你有所帮助。(=・ω・=)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/80286.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

本地完成Vue脚手架和Django建立连接

目录 在Vue中 setting.py中: urls.py中 首先把要连接的Django项目和Vue脚手架创建好 之后我们把整个Vue拖到Django的文件夹根目录下,于manage.py同级即可(图中data-work为我的Vue) 在Vue中 进入到vue.config.js文件夹下 添加as…

全栈Jmeter接口测试(三):jmeter利用察看结果树查看响应调试取样器(Debug Sampler),设置HTTP信息头管理器模拟请求头

Jmeter(5):jmeter利用察看结果树查看响应&调试取样器(Debug Sampler) 察看结果树选项介绍: 名称:本属性用于标识一个察看结果树元件,建议使用一个有意义的名称 注释:对于测试没有任何作用,仅用户记录用…

初级西班牙语教程

初级西班牙语教程 通过使用我的简化方法变得会话和流利的完整指南学习西班牙语 课程英文名:Spanish Made Simple Beginner Spanish 此视频教程共28.0小时,中英双语字幕,画质清晰无水印,源码附件全 下载地址 课程编号&#xff…

MySQL MVCC详解

为什么需要MVCC 在没有MVCC之前,是使用读写锁(共享锁/排它锁)来进行并发控制的,读锁和读锁之间不互斥,写锁和读锁互斥,写锁和写锁互斥。 但是频繁加锁会导致数据库性能低下,这时出现了一种不加…

数字脉冲参数

脉冲幅度vm。脉冲电压波形变化的最大值,单位为伏(v)。脉冲上升时间tr。脉冲波形从0.1vm上升到0.9vm所需的时间。脉冲下降时间tf。脉冲波形从0.9vm下降到0.1vm所需的时间。 脉冲上升时间tr和下降时间tf越短,越接近于理想的短形脉冲…

Redis实战——Redisson分布式锁

目录 1 基于Redis中setnx方法的分布式锁的问题 2 Redisson 2.1 什么是Redisson 2.2 Redisson实现分布式锁快速入门 2.3 Redisson 可重入锁原理 什么是可重入锁? Redisson中又是如何实现的呢? 2.4 Redisson分布式锁的可重试性 2.5 Redisson分布式锁的主从…

【C语言经典题目】调整奇数偶数顺序、有序序列合并以及有序序列判断

目录 一、调整奇数偶数顺序 1.思路一(使用多个数组) ①使用两个数组(双指针法) ②使用三个数组 2.思路二(不创建其他的数组,双指针) 二、有序数组合并 1.思路一 2.思路二 三、有序序列判…

【springboot进阶】基于starter项目构建(二)构建starter项目-mysql

目录 一、创建 mysql-spring-boot-starter 项目 二、添加 pom 文件依赖 三、构建配置 1. mybatis-plus分页配置 MybatisPlusConfig 2. mybatis-plus代码生成器 CodeGenerator 四、加载自动化配置 五、打包 六、使用 这个系列讲解项目的构建方式,主要使用 父…

第二证券|事关A股!4万亿外资巨头最新研判

时值年末,在多重利好音讯提振下,我国股市迎来一波反弹,海外本钱大举加仓我国财物。下一年全球经济将走向何方?国内和海外商场又会有哪些变化?财物装备该怎样做?近期,联博资深商场策略师黄森玮、…

PyTorch - Cifar 数据集

文章目录项目说明cifar-10 数据集介绍代码实现构建数据集、加载器构建 卷积网络训练数据构建 VGG 加深网络训练测试项目说明 cifar-10 数据集介绍 cifar-10 数据集由 60000 张分辨率为 32x32 彩色图像组成; 共分为 10 类,每类包含 6000 张图像&#xff…

深入理解mysql的索引分类,覆盖索引,覆盖索引失效,回表,MRR

MySql系列整体栏目 内容链接地址【一】深入理解mysql索引本质https://blog.csdn.net/zhenghuishengq/article/details/121027025【二】深入理解mysql索引优化以及explain关键字https://blog.csdn.net/zhenghuishengq/article/details/128273593【三】深入理解mysql的索引分类&a…

odps创建周期任务及字符串与日期函数用法

odps简介 PB/EB级数据的离线存储(存储资源)及逻辑处理(计算资源),集群可用性全托管。 MaxCompute(odps)是适用于数据分析场景的企业级SaaS(Software as a Service)模式云数据仓库,以Serverless架构提供快速、全托管的在线数据仓库…

[附源码]Python计算机毕业设计SSM基于的民宿租赁系统(程序+LW)

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

STS安装反编译插操作

第一步:下载net.sf.jadclipse_3.3.0.ja离线插件 首先​​​​​​下载离线插件 JadClipse - Eclipse plugin download | SourceForge.netDownload JadClipse - Eclipse plugin for free. Jad Java decompiler plugin for Eclipse IDE.https://sourceforge.net/proj…

Java自定义注解实现

一、注解的定义和作用 1、定义   注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面&…

51的原理图和pcb的注意事项及出现的问题与解决办法

本次总结基于立创eda的注意事项总结,如有疑问,欢迎交流。 1.焊盘设成地网络!!!! 2.pcb的基本步骤是布局---布线---泪滴---铺铜---缝合孔,记得泪滴和缝合孔!! 3.元器件的封装要选择贴片的 这样…

认识阻塞队列

认识阻塞队列一、相关概念1.1 阻塞队列是什么1.2 生产者消费者模型二、标准库中的阻塞队列2.1 使用2.2 生产者消费者模型实现三、实现阻塞队列3.1 循环队列3.2 实现的细节3.3 代码一、相关概念 1.1 阻塞队列是什么 阻塞队列是一种特殊的队列,也遵守 “先进先出” …

CENTOS上的网络安全工具(十六)容器特色的Linux操作

这一篇,我们继续在Docker上折腾。之前我们已经展示了如何在容器上搭建安全产品的部署环境,这里我们需要更进一步,讨论如何在容器上搭建开发与调试环境。这是学习安全产品并且自己构建安全产品的基础步骤。 〇、精简系统上的操作技巧 使用Dock…

【LeetCode】直线上最多的点数 [H](几何)

149. 直线上最多的点数 - 力扣(LeetCode) 一、题目 给你一个数组 points ,其中 points[i] [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。 示例 1: 输入:points [[1,1],[2,2],[3,3]] 输出&a…

【数据结构与算法分析】0基础带你学数据结构与算法分析11--AVL树

目录 二分查找 AVL 树 AVL 的平衡因子 AVL 的插入操作 单旋转 双旋转 对 AVL 树插入的总结 AVL 的移除操作 如果给定一个序列,你将如何在这个序列中查找一个给定元素 target,当找到时返回该元素的迭代器,否则返回末尾迭代器。首先排除…