`
wankunde
  • 浏览: 162303 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Java序列化与ProtocalBuffer序列化之深入分析

 
阅读更多

转载:http://kangsg219.iteye.com/blog/904762

 

最近在做交易缓存化项目,要求缓存淘宝15天内的所有交易订单、支付订单和物流订单,数量在2-3亿;这就对空间和时间提出了很高的要求,因此研究并测试了几种序列化和压缩技术,下面是对Java序列化与ProtocalBuffer序列化对象生成的字节码的分析,通过分析能很明显地看出ProtocalBuffer序列化的优势所在。

 

从一个简单对象的序列化内容来看java序列化与ProtocalBuffer序列化机制的不同之处以及优劣所在。对象准备如下:

 

父类BaseUserDO.java(gettersetter方法省去)

package serialize.compare;

public class BaseUserDO implements Serializable{

    private static final long serialVersionUID = 5699113544108250452L;

    private int pid;

}

 

子类UserDO.java继承上面的父类

package serialize.compare;

public class UserDO extends BaseUserDO{

    private static final long serialVersionUID = 6532984488602164707L;

    private int id;

    private String name;

}

 

New一个准备序列化的对象

UserDO user = new UserDO();

user.setPid(10);

user.setId(300);

user.setName("JavaSerialize ");//PbSerialize

 

 

Java序列化生成的16进制字节码共151个,内容如下:

 

AC ED 00 05 73 72 00 18 73 65 72 69 61 6C 69 7A 65 2E 63 6F 6D 70 61 72 65 2E 55 73 65 72 44 4F 5A A9 D2 C7 76 44 DD E3 02 00 02 49 00 02 69 64 4C 00 04 6E 61 6D 65 74 00 12 4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B 78 72 00 1C 73 65 72 69 61 6C 69 7A 65 2E 63 6F 6D 70 61 72 65 2E 42 61 73 65 55 73 65 72 44 4F 4F 17 51 92 BB 30 95 54 02 00 01 49 00 03 70 69 64 78 70 00 00 00 0A 00 00 01 2C 74 00 0D 4A 61 76 61 53 65 72 69 61 6C 69 7A 65

 

ProtocalBuffer序列化生成的16进制字节码只有18个,内容如下:

 

08 0A 10 AC 02 1A 0B 50 62 53 65 72 69 61 6C 69 7A 65

 

15118 差距不言而喻!下面分别分析一下这两段字节码的内容,先看java序列化的内容:

 

Java序列化一个对象产生的字节码是自描述型的,也就是说不借助其他的信息,仅仅从它本身的内容就能够找出这个对象的所有信息,比如说类元数据描述、类的属性、属性的值以及父类的所有信息。

Java序列化是将这些信息分成3个部分:

1.开头部分(颜色表示的都是常量,在java.io.ObjectStreamConstants 类中)

AC ED:写入流的幻数,STREAM_MAGIC;

00 05:写入流的版本号,STREAM_VERSION;

 

2.类描述部分(包括父类的描述信息)

73:TC_OBJECT, 声明这是一个新的对象;

72:TC_CLASSDESC,声明这里开始一个新Class;

00 18:class类名的长度(也就是”serialize.compare.UserDO”的长度);

73 65 72 69 61 6C 69 7A 65 2E 63 6F 6D 70 61 72 65 2E 55 73 65 72 44 4F:这24个字节码转化成字符串就是:”serialize.compare.UserDO”;

5A A9 D2 C7 76 44 DD E3 serialVersionUID = 6532984488602164707L,

(如果没有serialVersionUID会随机生成一个);

02:标记号,该值表示该对象支持序列化;

00 02:该类所包含属性的个数(idname);

49:字符“I”的值,代表属性的类型,也就是int;

00 02:属性名称的长度;(“id”.length()==2);

69 64:属性名称:id;

4C字符“L”的值,代表属性的类型,不知道为什么用L来表示String

00 04:属性名称的长度;(“name”.length()==2);

6E 61 6D 65:属性名称:”name”;

74TC_STRING,代表一个new String,这里是用来引用父类BaseUserDO;

00 12:对象签名的长度;

4C 6A 61 76 61 2F 6C 61 6E 67 2F 53 74 72 69 6E 67 3B: Ljava/lang/String;

78:TC_ENDBLOCKDATA对象块结束的标志,7478之间的内容是用来说明UserDO BaseUserDO之间的继承关系的。

72TC_CLASSDESC,声明这里开始一个新Class;即父类BaseUserDO;

00 1Cclass类名的长度(也就是”serialize.compare.BaseUserDO”的长度);

73 65 72 69 61 6C 69 7A 65 2E 63 6F 6D 70 61 72 65 2E 42 61 73 65 55 73 65 72 44 4F”serialize.compare.BaseUserDO”;

4F 17 51 92 BB 30 95 54: serialVersionUID=5699113544108250452L;

02标记号,该值表示该对象支持序列化;

00 01该类所包含属性的个数(pid);

49字符“I”的值,代表属性的类型,也就是int;

00 03:属性名称的长度;

70 69 64:”pid”;

78: TC_ENDBLOCKDATA对象块结束的标志;

70TC_NULL,说明没有其他超类的标志;

 

3.属性值部分:(从父类开始将实例对象的实际值输出)

        00 00 00 0A:10;(int pid = 10;)

    00 00 01 2C:300;(int id = 300;)

    74: TC_STRING;说明下面这个值的类型是String型的;

    00 0D:这个字符串的长度是13;

    4A 61 76 61 53 65 72 69 61 6C 69 7A 65:”JavaSerialize”;

从上面的解析可以看出序列化的内容大部分到在描述自己,而我们关心的值的部分即红颜色的部分只暂很小的一个部分,21个字节,占比13.91%。空间浪费严重。

 

再来看下ProtocalBuffer序列化,Pb在序列化之前需要定义一个.proto文件,用于描述一个message的数据结构,如下:

package tutorial;

 

option java_package = "serialize.compare";

option java_outer_classname = "UserAgent";

 

message UserDO{

    optional int32 pid = 1;

    optional int32 id = 2;

    optional string name = 3;

}

再使用PB的编译器编译这个.proto文件生成一个代理类,即UserAgent.java,对象的序列化和反序列化就通过这个代理类来实现;对象经过序列化后会成为一个二进制数据流,该流中的数据为一系列的 Key-Value 对,非常紧凑。如下图所示:


Key:是由公式计算出来的:(field_number << 3) | wire_type;

Value:是进过编码处理过的字节码;包括:Varintzigzag;

wire_type对应表:

Type

Meaning

Used For

0

Varint

int32, int64, uint32, uint64, sint32, sint64, bool, enum

1

64-bit

fixed64, sfixed64, double

2

Length-delimi

string, bytes, embedded messages, packed repeated fields

3

Start group

Groups (deprecated)

4

End group

Groups (deprecated)

5

32-bit

fixed32, sfixed32, float

 

Varint 是一种紧凑的表示数字的方法。它用一个或多个字节来表示一个数字,值越小的数字使用越少的字节数。这能减少用来表示数字的字节数。比如对于 int32 类型的数字,一般需要 4 个 byte 来表示。但是采用Varint,对于很小的 int32 类型的数字,则可以用 1 个 byte 来表示。当然凡事都有好的也有不好的一面,采用Varint 表示法,大的数字则需要 5 个 byte 来表示。从统计的角度来说,一般不会所有的消息中的数字都是大数,因此大多数情况下,采用 Varint 后,可以用更少的字节数来表示数字信息。对于带符号的整数则是采用zigzag编码来处理,这样避免用一个很大的二进制数来表示一个负数,对应的wire_type是sint32,sint64.

 

了解了以上知识后就可以开解析一下以下序列化内容了:

08 0A 10 AC 02 1A 0B 50 62 53 65 72 69 61 6C 69 7A 65 

08 0A:这是一个key-value对,08key,(1<<3)|0计算得出;
0Avalue,因为采用Varint编码10只需要一个字节来表示;java序列化中则是用4个字节来表示:00 00 00 0A
10 AC 02: key(2<<3)|0计算得出;value也是采用Varint编码;
演算一下:AC > 10*16+12 > 172 > 128+32+8+4 > 1010 1100
(高位是1说明还没有结束,下一个字节也是这个值的一部分)
02 > 0000 0010 (高位是0说明结束)
> 1010 1100   0000 0010
> 010 1100 000 0010(去掉高位,因为高位只是个标记位)
> 000 0010 010 1100(little-endian互换位置)
> 100101100 (二进制)
> 300 (十进制)
 
1A0B 50 62 53 65 72 69 61 6C 69 7A 65
1Akey:(3<<3)|2 (stringwire_type=2参照上面的表格)
0B:表示这个string的长度为11
50 62 53 65 72 69 61 6C 69 7A 65:string的内容:”PbSerialize”;
 
可以看出这些字节码中没有任何类元素的描述,属性也是用tag来表示,
而且int32int64number型都采用了Varint编码,
我们的业务对象很多属性都是用int型的用来表示各种状态,而且值都是0,1,2,3之类的少于128的值,
那么这些value都只需用一个字节来存储,大大减少了空间。Value 占比也非常高:达到了77.78%。
               

 

分享到:
评论

相关推荐

    Python调用序列化数据工具Protocol Buffers——protobuf

    **Python调用序列化数据工具Protocol Buffers(protobuf)详解** Protocol Buffers是Google开发的一种高效的数据序列化协议,它允许开发者将结构化的数据串行化,可用于数据存储、通信协议等方面。Python作为一门...

    命名管道结合protocol buffer 进行本地通讯

    响应同样需要使用Protocol Buffer进行序列化和反序列化。 6. 错误处理:在实际应用中,还需要考虑异常处理,如连接断开、数据解析错误等情况,确保通信的健壮性。 示例代码中"ClientTest"和"ServerTest"可能是用...

    jersey和Google Protocol Buffer开发Demo

    【描述】:本示例将深入探讨如何使用Jersey,一个强大的Java RESTful Web服务框架,与Google Protocol Buffers(简称PB)结合,进行高效的数据序列化。这个Demo是实际开发中的一个实用案例,展示了这两个技术在实际...

    HadoopCommon包分析1

    此外,Java序列化不支持跨语言操作,并且在反序列化时会不断创建新对象,增加JVM的压力。 为了解决这些问题,Hadoop引入了自己的序列化机制,称为Writable接口。Writable接口提供了一种紧凑、快速的序列化方式,...

    Protocol Buffers 学习笔记

    4. **日志记录**:结构化的日志数据可以通过protobuf进行序列化,便于分析和检索。 ### 三、protobuf的优点 1. **高效**:protobuf的二进制编码方式比XML、JSON更节省空间和时间。 2. **跨平台**:支持多种编程...

    Java开发详解.zip

    031217_【第12章:JAVA IO】_对象序列化笔记.pdf 031218_〖第12章:JAVA IO〗_实例操作—单人信息管理程序笔记.pdf 031219_〖第12章:JAVA IO〗_实例操作:投票程序笔记.pdf 031301_【第13章:Java类集】_认识类集、...

    Java学习之IO总结及mina和netty

    首先,Java IO提供了丰富的类库,允许程序进行数据的读写操作,包括文件操作、流处理、序列化等。它基于流的概念,分为字节流和字符流,分别处理字节数据和字符数据。字节流包括InputStream和OutputStream作为基类,...

    pbdcompiler_pbdcompiler_

    【PBDCompiler:深入解析与应用】 PBDCompiler是一个关键的工具,主要用于处理和编译PBD(Protocol Buffer Data)文件。在IT行业中,尤其是在大数据处理、网络通信和分布式系统中,PBD编译器扮演着至关重要的角色。...

    java udp编程

    Java UDP编程是网络通信领域中的一个重要话题,它涉及到网络编程的基础知识和Java的Socket编程接口。UDP(User Datagram Protocol)...通过分析和运行这些代码,你可以更深入地理解UDP通信的工作原理和Java中如何实现。

    java写的UDP源码

    - 准备数据,通常用字节数组表示,可以是字符串、对象等,通过序列化转换成字节流。 - 创建`DatagramPacket`对象,传入数据、目标IP地址和端口号。 - 使用`DatagramSocket`的`send()`方法发送`DatagramPacket`。 ...

    protobuf source v3.21.12

    protobuf是Protocol Buffers的缩写,它是一种高效的数据序列化协议,由Google开发并开源。Protocol Buffers允许开发者定义数据结构的模式(schema),然后生成相应的语言绑定(如C++、Java、Python),使得应用程序...

    java技术分享

    ### Java技术分享知识点详解 #### 一、技术讨论 ##### 1.1 OFFHEAP -- 老宋分享 ...以上知识点覆盖了Java技术分享中的主要内容,包括技术讨论、体系架构及开源系统等方面,旨在为读者提供全面深入的理解。

    java学习之英文词汇

    12. **Analysis**: 分析,对数据进行深入研究的过程。 13. **Relation**: 关系,数据库中的表之间存在的关联。 14. **Schema**: 模式,数据库的结构设计。 15. **Query**: 查询,从数据库中检索数据的命令。 16. **...

    java源码:高性能Java网络框架 MINA.zip

    例如,它可以将Java对象序列化为XML或JSON格式进行网络传输。 5. **Event-driven Model**: MINA使用事件驱动模型,当网络事件发生时,如连接建立、数据到达、连接关闭等,会触发相应的事件处理器。 6. **Transport...

    CatServer_聊天室_CatServer_

    这些功能的实现通常涉及对输入/输出流的读写操作,消息的序列化与反序列化,以及线程同步技术,以避免数据竞争和确保消息的正确传递。 CatServer项目可能包含以下关键组件: 1. Server类:作为主入口,初始化...

    android UDP socket通讯,演示demo

    在Android平台上进行网络通信时,UDP(User Datagram Protocol)socket是一种常用的方式,它提供了一种无...同时,注意在实际应用中,应考虑数据的序列化和反序列化、错误处理以及性能优化,以确保稳定可靠的通信体验。

    微信IPAD 协议 微信接口 WeChatApi

    `protobuf-net.xml`可能是关于Protocol Buffers(protobuf)的序列化库的配置文件,protobuf是Google推出的一种数据交换的序列化协议,它高效、跨平台且兼容性强,常用于网络通信的数据传输。 `aspnet_client`目录...

    Android perfetto tools

    这个配置文件是以Protocol Buffer(protobuf)格式编码的,它是Google开发的一种轻量级、跨平台的数据序列化协议,便于数据交换和解析。 要自定义追踪配置,开发者需要创建或修改`perfetto_config.pbtx`文件。例如...

    phython api document

    - **Data Marshalling Support**:讲解了数据序列化和反序列化的支持,这对于网络通信非常重要。 - **Parsing Arguments and Building Values**:解释了如何解析命令行参数并构建相应的值。 - **String ...

    Android_UDPBroadcast_Demo

    当收到数据时,可以反序列化数据并进行处理: ```java DatagramSocket socket = new DatagramSocket(5000); // 监听5000端口 byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer,...

Global site tag (gtag.js) - Google Analytics