`

(转)加快Java的文件序列化速度 .

 
阅读更多

源地址:http://blog.csdn.net/maritimesun/article/details/8065143

 

自从第一个Java版本开始,很多开发人员一直都在尝试让Java获得最少和C/C++一样的表现。JVM提供商尽他们最大的努力去实现一些新的JIT算法,但是还是有很多需要做的,特别是在我们使用Java的方法上。

例如,在对象<->文件序列化上就差距很大--尤其在读写内存对象上。我将就这个主题做一些解释和分享。

所有的测试都是在下面这个对象上执行的:

1 public class TestObject implements Serializable {
2  
3   private long longVariable;
4   private long[] longArray;
5   private String stringObject;
6   private String secondStringObject; //just for testing nulls
7  
8   /* getters and setters */
9 }

 

 

为了简单起见,我将只贴出写入方法(尽管读取类似),完整的源码在GitHub上可以找到(http://github.com/jkubrynski/serialization-tests

最标准的java序列化(我们都是从这里学起的)是这样的:

01 public void testWriteBuffered(TestObject test, String fileName) throwsIOException {
02   ObjectOutputStream objectOutputStream = null;
03   try {
04     FileOutputStream fos = new FileOutputStream(fileName);
05     BufferedOutputStream bos = new BufferedOutputStream(fos);
06     objectOutputStream = new ObjectOutputStream(bos);
07     objectOutputStream.writeObject(test);
08   finally {
09     if (objectOutputStream != null) {
10       objectOutputStream.close();
11     }
12   }
13 }

 

 

提升标准序列化速度的最简单方法时使用RandomAccessFile对象:

01 public void testWriteBuffered(TestObject test, String fileName) throwsIOException {
02   ObjectOutputStream objectOutputStream = null;
03   try {
04     RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
05     FileOutputStream fos = new FileOutputStream(raf.getFD());
06     objectOutputStream = new ObjectOutputStream(fos);
07     objectOutputStream.writeObject(test);
08   finally {
09     if (objectOutputStream != null) {
10       objectOutputStream.close();
11     }     
12 }

 

 

更高深点的技术是使用Kryo框架,新旧版本的差距是很大的,我做过测试。因为性能比较上并没有体现出特别引人注意的差异,所以我将使用2.x版本,因为它对用户更友好而且更快些。

01 private static Kryo kryo = new Kryo(); // version 2.x
02  
03 public void testWriteBuffered(TestObject test, String fileName) throwsIOException {
04   Output output = null;
05   try {
06     RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
07     output = new Output(new FileOutputStream(raf.getFD()), MAX_BUFFER_SIZE);
08     kryo.writeObject(output, test);
09   finally {
10     if (output != null) {
11       output.close();
12     }
13   }
14 }

 

 

最后一个方案是在Martin Thompson的文章中提到的(Native C/C++ Like Performance For Java Object Serialisation),介绍了怎样在Java中像C++那样和内存打交道。

01 public void testWriteBuffered(TestObject test, String fileName) throwsIOException {
02   RandomAccessFile raf = null;
03   try {
04     MemoryBuffer memoryBuffer = new MemoryBuffer(MAX_BUFFER_SIZE);
05     raf = new RandomAccessFile(fileName, "rw");
06     test.write(memoryBuffer);
07     raf.write(memoryBuffer.getBuffer());
08   catch (IOException e) {
09     if (raf != null) {
10       raf.close();
11     }
12   }
13 }

 

 

TestObject写入方法如下:

01 public void write(MemoryBuffer unsafeBuffer) {
02   unsafeBuffer.putLong(longVariable);
03   unsafeBuffer.putLongArray(longArray);
04   // we support nulls
05   boolean objectExists = stringObject != null;
06   unsafeBuffer.putBoolean(objectExists);
07   if (objectExists) {
08     unsafeBuffer.putCharArray(stringObject.toCharArray());
09   }
10   objectExists = secondStringObject != null;
11   unsafeBuffer.putBoolean(objectExists);
12   if (objectExists) {
13     unsafeBuffer.putCharArray(secondStringObject.toCharArray());
14   }
15 }

 

 

直接内存缓冲区类(已简化了的,仅仅为了展示这个思想)

01 public class MemoryBuffer {
02   // getting Unsafe by reflection
03   public static final Unsafe unsafe = UnsafeUtil.getUnsafe();
04  
05   private final byte[] buffer;
06  
07   private static final long byteArrayOffset = unsafe.arrayBaseOffset(byte[].class);
08   private static final long longArrayOffset = unsafe.arrayBaseOffset(long[].class);
09   /* other offsets */
10  
11   private static final int SIZE_OF_LONG = 8;
12   /* other sizes */
13  
14   private long pos = 0;
15  
16   public MemoryBuffer(int bufferSize) {
17     this.buffer = new byte[bufferSize];
18   }
19  
20   public final byte[] getBuffer() {
21     return buffer;
22   }
23  
24   public final void putLong(long value) {
25     unsafe.putLong(buffer, byteArrayOffset + pos, value);
26     pos += SIZE_OF_LONG;
27   }
28  
29   public final long getLong() {
30     long result = unsafe.getLong(buffer, byteArrayOffset + pos);
31     pos += SIZE_OF_LONG;
32     return result;
33   }
34  
35   public final void putLongArray(final long[] values) {
36     putInt(values.length);
37     long bytesToCopy = values.length << 3;
38     unsafe.copyMemory(values, longArrayOffset, buffer, byteArrayOffset + pos, bytesToCopy);
39     pos += bytesToCopy;
40   }
41  
42  
43   public final long[] getLongArray() {
44     int arraySize = getInt();
45     long[] values = new long[arraySize];
46     long bytesToCopy = values.length << 3;
47     unsafe.copyMemory(buffer, byteArrayOffset + pos, values, longArrayOffset, bytesToCopy);
48     pos += bytesToCopy;
49     return values;
50   }
51  
52   /* other methods */
53 }

 

 

几个小时的Caliper测试结果如下:

 

  Full trip [ns]  Standard deviation [ns] 
Standard   207307  2362
Standard on RAF  42661  733
KRYO 1.x   12027  112
KRYO 2.x  11479  259
Unsafe  8554  91

 

在最后我们可以得出一些结论:

  • Unsafe序列化比标准的java.io.Serizlizable快了23倍
  • 使用RandomAccessFile可以使标准的有缓冲序列化加速将近4倍
  • Kryo-dynamic序列化大约比手写实现的直接缓冲满了35%

最后,就像我们看到的那样,还是没有绝对的答案。对于我们中的大多数人来说,获得3000ns(0.003ms)的速度提升是不值得为每个需要序列化的对象来写单独实现的。在标准的方案中,我们大多数选择Kryo 。然而,在惜时如金的低延时系统中,这个选择将会是完全不同的。

分享到:
评论

相关推荐

    加快Java的文件序列化速度

    以下是一些加快Java文件序列化速度的方法: 1. **优化对象结构**: - **减少嵌套深度**:如果对象层次太深,序列化和反序列化时会增加额外的时间开销。尽量简化对象结构,避免过多的嵌套。 - **精简对象属性**:...

    Java文件传输

    它们允许序列化和反序列化Java对象,使得对象能在网络间传输。 5. **NIO(New Input/Output)**: Java NIO(非阻塞I/O)提供了一种新的方式来处理I/O操作,尤其是对于高并发的网络应用。它使用Channel和Selector...

    Python-MessagePackPython是用于Python的MessagePack序列化实现

    MessagePack就是一种高效、跨语言的数据序列化库,它专为速度和效率而设计,特别适合处理大数据量的场景。 **MessagePack简介** MessagePack是一种轻量级的二进制序列化格式,它的目标是比JSON更快、更小。在保持...

    Presenting Java Beans (Sams).pdf

    本章将讨论如何实现JavaBeans的序列化和反序列化。 #### 8. 定制:为应用程序构建者提供Bean支持 为了让Bean更加灵活和易于使用,开发人员可能会添加额外的支持,如图形界面或配置选项。这部分内容将展示如何为...

    Java SuDoKu数独游戏.rar

    4. **SaveLoadManager**:保存和加载游戏进度的功能类,可能实现了序列化技术,将游戏状态转化为文件,以便用户可以随时继续游戏。 5. **Solver**:如果游戏包含自动解决功能,这个类将包含高级算法,如回溯法或...

    Java RMI分布式系统应用研究.pdf

    分布式计算是现代信息技术中的一个重要研究领域,它涉及将计算任务拆分成多个子任务,并在不同计算机上进行并行处理以加快整体计算速度。由于单台计算机的计算能力有限,分布式计算模型在处理大型复杂问题时表现出其...

    protoc jar包

    protoc jar文件用于编译.proto文件,将定义的协议消息类型转换为Java类,而Java运行时库则是在应用程序中处理序列化和反序列化的必要依赖。 在Android开发中,protobuf被广泛应用于服务器与客户端之间的数据交换,...

    java报表jfreechart开发

    ### Java报表JFreeChart开发知识点总结 #### 1. 简介 ##### 1.1 什么是JFreeChart JFreeChart是一款免费且开源的Java图表库,它提供了多种类型的图表绘制功能,如饼图、条形图、折线图等。JFreeChart适用于各种Java...

    protobuf-all-3.6.1

    开发者可以使用protobuf编译器将.proto文件转换为目标语言(如C++、Java、Python等)的源代码,生成的类库提供了序列化和反序列化的API,使得数据可以在不同系统之间进行高效通信。 在“protobuf-all-3.6.1.zip”这...

    leveldb,leveldb到java的端口.zip

    要将leveldb移植到Java平台,通常需要实现一个Java版的leveldb接口,这包括数据序列化、存储管理和读写操作等功能。移植过程主要包括以下步骤: 1. 编译JNI库:首先,需要编译出适用于Java的leveldb JNI库,这涉及到...

    JAVA爬虫项目源代码

    3. **FastJson**:FastJson是阿里巴巴开源的一款高性能的JSON库,用于JSON的序列化和反序列化。在爬虫项目中,FastJson可以帮助将抓取到的JSON格式数据快速转换为Java对象,便于存储和后续处理。同时,它也能将Java...

    Protocol Buffer

    2. **编译器**:`protoc`是Protocol Buffer提供的编译器,它可以将.proto文件编译成不同编程语言(如C++、Java、Python等)的源代码,生成的类库提供了序列化和反序列化的接口。 3. **序列化与反序列化**:Protocol...

    ILCD-to-RDF:将ILCD XML序列化中的LCA数据转换为(大致等效)RDFXML序列化

    从LCD到RDF 将ILCD XML序列化中的LCA数据转换为(大致等效)RDF / XML序列化ILCD模式非常大,此代码仅转换很小的子集,足以表示进程及其输入和输出流。 这样做的主要动机是探索关联数据和LCA的机会和问题。依存关系...

    象棋java版,实现悔棋 保存等功能

    - 使用Java的`ObjectOutputStream`和`ObjectInputStream`类可以序列化和反序列化对象,将棋盘状态保存到文件中,然后在需要时读取。保存时,将棋盘、棋子、玩家等核心对象写入文件;加载时,从文件读取这些信息恢复...

    JAVA+SQL办公自动化系统(源代码+设计说明书+).zip

    5. **索引优化**:为了加快查询速度,数据库可能会创建索引,设计者需要考虑如何有效地利用索引来提升系统的性能。 **毕业设计相关** 作为毕业设计,这个项目可能包含以下环节: 1. **需求分析**:对办公自动化...

    Ehcache分布式缓存入门案例demo_文件转树结构_

    Ehcache是一个高性能、轻量级的Java分布式缓存库,它被广泛应用于提升应用程序的性能,通过存储经常访问的数据来减少对数据库的依赖,从而加快系统的响应速度。本入门案例将带你了解如何使用Ehcache实现分布式缓存,...

    JAVA-memcached简单例子

    - 序列化:默认情况下,spymemcached使用Java序列化。但你可以自定义序列化策略,比如使用Gson或Jackson库进行JSON序列化,以提高性能和可读性。 - 批量操作:支持批量设置、获取和删除键值对,减少网络往返次数,...

    google protobuf-2.4.1

    - **存储优化**:在数据库或文件系统中,protobuf序列化的数据可以减少存储空间,加快读写速度。 5. **优势与局限**: - **优点**:效率高,占用空间小,支持多种语言,易于扩展和维护。 - **局限**:学习曲线较...

    马得智.zip

    7. **互联网传输**:ZIP文件常用于互联网上传输,因为它们可以减小文件大小,加快下载速度。 8. **归档用途**:ZIP文件也常用于备份和归档,将大量文件整合在一起,方便存储和恢复。 9. **开发工具集成**:许多...

Global site tag (gtag.js) - Google Analytics