`
yanguz123
  • 浏览: 577892 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

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

 
阅读更多

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

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

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

public class TestObject implements Serializable {

  private long longVariable;
  private long[] longArray;
  private String stringObject;
  private String secondStringObject; //just for testing nulls

  /* getters and setters */
}

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

一、最标准的java序列化(我们都是从这里学起的)是这样的:
public void testWriteBuffered(TestObject test, String fileName) throws IOException {
  ObjectOutputStream objectOutputStream = null;
  try {
    FileOutputStream fos = new FileOutputStream(fileName);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    objectOutputStream = new ObjectOutputStream(bos);
    objectOutputStream.writeObject(test);
  } finally {
    if (objectOutputStream != null) {
      objectOutputStream.close();
    }
  }
}

二、提升标准序列化速度的最简单方法时使用RandomAccessFile对象:
public void testWriteBuffered(TestObject test, String fileName) throws IOException {
  ObjectOutputStream objectOutputStream = null;
  try {
    RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
    FileOutputStream fos = new FileOutputStream(raf.getFD());
    objectOutputStream = new ObjectOutputStream(fos);
    objectOutputStream.writeObject(test);
  } finally {
    if (objectOutputStream != null) {
      objectOutputStream.close();
    }     
}

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

private static Kryo kryo = new Kryo(); // version 2.x

public void testWriteBuffered(TestObject test, String fileName) throws IOException {
  Output output = null;
  try {
    RandomAccessFile raf = new RandomAccessFile(fileName, "rw");
    output = new Output(new FileOutputStream(raf.getFD()), MAX_BUFFER_SIZE);
    kryo.writeObject(output, test);
  } finally {
    if (output != null) {
      output.close();
    }
  }
}

四、最后一个方案是在Martin Thompson的文章中提到的(Native C/C++ Like Performance For Java Object Serialisation),介绍了怎样在Java中像C++那样和内存打交道。
public void testWriteBuffered(TestObject test, String fileName) throws IOException {
  RandomAccessFile raf = null;
  try {
    MemoryBuffer memoryBuffer = new MemoryBuffer(MAX_BUFFER_SIZE);
    raf = new RandomAccessFile(fileName, "rw");
    test.write(memoryBuffer);
    raf.write(memoryBuffer.getBuffer());
  } catch (IOException e) {
    if (raf != null) {
      raf.close();
    }
  }
}
TestObject写入方法如下:
public void write(MemoryBuffer unsafeBuffer) {
  unsafeBuffer.putLong(longVariable);
  unsafeBuffer.putLongArray(longArray);
  // we support nulls
  boolean objectExists = stringObject != null;
  unsafeBuffer.putBoolean(objectExists);
  if (objectExists) {
    unsafeBuffer.putCharArray(stringObject.toCharArray());
  }
  objectExists = secondStringObject != null;
  unsafeBuffer.putBoolean(objectExists);
  if (objectExists) {
    unsafeBuffer.putCharArray(secondStringObject.toCharArray());
  }
}
直接内存缓冲区类(已简化了的,仅仅为了展示这个思想)
public class MemoryBuffer {
  // getting Unsafe by reflection
  public static final Unsafe unsafe = UnsafeUtil.getUnsafe();

  private final byte[] buffer;

  private static final long byteArrayOffset = unsafe.arrayBaseOffset(byte[].class);
  private static final long longArrayOffset = unsafe.arrayBaseOffset(long[].class);
  /* other offsets */

  private static final int SIZE_OF_LONG = 8;
  /* other sizes */

  private long pos = 0;

  public MemoryBuffer(int bufferSize) {
    this.buffer = new byte[bufferSize];
  }

  public final byte[] getBuffer() {
    return buffer;
  }

  public final void putLong(long value) {
    unsafe.putLong(buffer, byteArrayOffset + pos, value);
    pos += SIZE_OF_LONG;
  }

  public final long getLong() {
    long result = unsafe.getLong(buffer, byteArrayOffset + pos);
    pos += SIZE_OF_LONG;
    return result;
  }

  public final void putLongArray(final long[] values) {
    putInt(values.length);
    long bytesToCopy = values.length << 3;
    unsafe.copyMemory(values, longArrayOffset, buffer, byteArrayOffset + pos, bytesToCopy);
    pos += bytesToCopy;
  }


  public final long[] getLongArray() {
    int arraySize = getInt();
    long[] values = new long[arraySize];
    long bytesToCopy = values.length << 3;
    unsafe.copyMemory(buffer, byteArrayOffset + pos, values, longArrayOffset, bytesToCopy);
    pos += bytesToCopy;
    return values;
  }

  /* other methods */
}

几个小时的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的文件序列化速度.pdf

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

    基于Java实现的高效序列化反序列化JSON框架设计源码

    其中,Java源文件构成了框架的核心逻辑部分,而XML和JSON文件可能被用来配置序列化规则和反序列化策略。Git忽略文件对于版本控制来说是必不可少的,它能够确保项目在使用Git作为版本控制系统时,一些不必要跟踪的...

    序列化案例文件用于统计每一个手机号耗费的总上行流量、总下行流量、总流量

    在大数据处理场景中,Hadoop序列化是一个重要的技术,它关系到数据在网络中传输、以及在Hadoop文件系统中存储时的效率和性能。序列化是指把对象转换为字节流的过程,反序列化则是将字节流恢复成对象的过程。Hadoop...

    Java文件传输

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

    基于Java语言的Huffman文件编解码与可视化树设计源码

    在当今数据爆炸的时代,高效的文件压缩技术对于节约存储空间、加快数据传输速度都至关重要。基于Java语言的Huffman文件编解码与可视化树设计源码,不仅仅是一个软件开发项目,更是一个教育工具和技术创新的体现,它...

    基于Java与JavaScript的高效文件传输服务器设计源码

    YAML配置文件则提供了一种更为简洁的数据序列化格式,便于配置数据的阅读和维护。HTML页面和JavaScript文件共同构成了用户交互的前端界面,使得用户可以方便地管理和操作文件传输任务。 此外,该项目还包含了一个...

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

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

    protoc jar包

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

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

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

    JAVA爬虫项目源代码

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

    基于springshardingmybaits集成redis缓存的游戏分布式存储框架支持将对象序列化到队列里异步存储K.zip

    该框架在技术栈上集成了Spring、ShardingSphere、MyBatis以及Redis,并通过创新性地将对象序列化并异步存储到队列中,实现了对游戏分布式存储的优化。 首先,Spring作为一个成熟的Java应用框架,其提供的轻量级和...

    基于Java语言的久**聚合SDK设计源码接入文档

    SER序列化文件用于Java对象的序列化和反序列化操作,保证了数据持久化的需要。Java源文件则是SDK功能实现的核心,包含了所有必要的Java类和接口定义,是开发者关注的重点。 整体来看,文档不仅是关于代码的简单说明...

    基于Java平台的通用基础模块设计源码

    这些文件共同构成了一个基础模块,它能够支持Spring容器初始化、配置文件读取、分页处理、Protobuf序列化、反射操作等关键功能。 Spring容器初始化是Java企业级开发中的一个重要环节,它负责创建和管理应用中的对象...

    基于Java核心的TJFramework Android快速开发框架设计源码

    属性文件和SER文件则通常包含项目配置信息和序列化数据。 TJFramework的设计理念是通过提供一套可复用的模板和库,减少开发中常见任务的代码编写量,从而加快开发速度。同时,由于框架是基于Java核心,它自然地继承...

    基于Java和Vue的西柚一卡通设计源码

    Git忽略文件则是版本控制中定义不纳入版本管理的文件列表,而YAML配置文件则用于简化数据序列化过程,JAR库文件则包含了Java程序运行所需的一些库。 Java源文件主要负责后端逻辑的实现,包括但不限于数据库的连接、...

    JAVA-memcached简单例子

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

    随手做一个多线程的 CS架构的 文件传输Demo

    Java的序列化、Base64编码或自定义的打包格式都可以用于此目的。 5. **异常处理**:在文件传输过程中可能出现各种错误,如网络中断、文件不存在等,因此需要适当的异常处理机制。 6. **状态同步**:多线程环境下,...

    基于Java和JavaScript的kcg开源代码生成器设计源码

    基于Java和JavaScript的kcg开源代码生成器设计源码是一个支持多数据中心的代码生成工具,其设计理念是为了加快开发速度,提高开发效率。该项目充分利用了Java和JavaScript语言的特性,利用Java的后端处理能力和...

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

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

Global site tag (gtag.js) - Google Analytics