- 浏览: 120538 次
- 性别:
- 来自: 上海
文章分类
最新评论
源地址: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) throws IOException { |
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) throws IOException { |
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) throws IOException { |
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) throws IOException { |
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 。然而,在惜时如金的低延时系统中,这个选择将会是完全不同的。
发表评论
-
(转)JDK工具(查看JVM参数、内存使用情况及分析等)
2018-12-25 15:50 317https://www.cnblogs.com/z ... -
[转]jstat查看jvm的GC情况
2018-12-25 15:38 519jstat 1. jstat -gc pid ... -
转一个 jmap 的基本使用方法
2017-04-05 11:52 504原文:http://hbluojiahui.bl ... -
(转)JVM内存堆布局图解分析
2017-04-05 11:56 366转载原文出处:http://www.codeceo.com/ ... -
(转)系统吞吐量(TPS)、用户并发量、性能测试概念和公式
2017-03-27 11:19 432PS:下面是性能测试的主要概念和计算公式,记录下: 一.系 ... -
(转)深入理解Major GC, Full GC, CMS
2016-11-02 11:27 470原文:http://blog.csdn.net/iter_ ... -
(转)OpenSSL 1.0.0生成p12、jks、crt等格式证书的命令个过程
2016-07-26 18:51 619OpenSSL 1.0.0生成p12、jks、crt等格式 ... -
(转)Java 内存区域和GC机制
2016-07-26 14:09 340录 Java垃圾回收概况 Java内存区域 Java ... -
Understanding CMS GC Logs
2016-07-26 11:06 502Understanding CMS GC Logs By ... -
(转)Java动态代理机制详解(JDK 和CGLIB,Javassist,ASM)
2016-07-08 17:56 561源地址:http://blog.csdn.net/lu ... -
linux下查看最占性能的JAVA进程
2016-03-08 11:58 600记录一下自己常用的linux系统命令,方便以后查阅,发觉记忆 ... -
(转,精)Java 多线程 并发编程
2015-10-10 19:50 773源地址:http://blog.csdn.n ... -
java虚拟机内存监控工具jps,jinfo,Jstack,jstat,jmap,jhat使用
2015-09-21 13:14 1158源地址:http://my.oschina. ... -
(转)JMM模型
2014-11-11 16:53 464源地址:http://blog.csdn.net/gt ... -
(转)《深入浅出 Java Concurrency》目录
2014-11-10 15:55 368原文地址:http://www.blogjava.net/x ... -
(转)TCP/IP、Http、Socket的区别
2014-08-21 10:32 790源地址: http://jingyan.baidu.com/ ... -
(转)Swift里的CAP理论和NWR策略应用
2014-08-12 17:25 588源地址:http://blog.sina.com.cn/s/ ... -
(转)Java多线程编程的常见陷阱
2014-06-25 13:14 476源地址:http://developer.51cto.com ... -
(转)Java 正确的做字符串编码转换
2014-03-11 21:52 620原文:http://hi.baidu.com/sodarf ... -
深入理解java内存模型系列文章
2013-12-30 10:57 415深入理解java内存模型系列文章 源地址: htt ...
相关推荐
自从第一个Java版本开始,很多开发人员一直都在尝试让Java获得最少和C/C++一样的表 现。JVM提供商尽他们最大的努力去实现一些新的JIT算法,但是还是有很多需要做的,特别 是在我们使用Java的方法上。
加快Java的文件序列化速度
从LCD到RDF 将ILCD XML序列化中的LCA数据转换为(大致等效)RDF / XML序列化ILCD模式非常大,此代码仅转换很小的子集,足以表示进程及其输入和输出流。 这样做的主要动机是探索关联数据和LCA的机会和问题。依存关系...
包括支持Java语言中的基本类型、违例处理等,支持java.lang包和java.util包中的绝大部分类和接口,支持正则表达式和序列化。 2. 跨平台支持 如果你使用GWT中提供的显示组件(比如Button)和组装组件(比如VerticalPanel...
·使用Java序列化存储版本和提交信息。 ·多线程可加快文件操作。 ·在VCS中提取最新版本。 ·管理分支机构的操作。 ·文件管理以实现最佳存储。 要做的工作使用cmd版本在Java swing中使其成为可用的GUI
HBase可以支持N ative Java API、HBase Shell等多种访问接口,可以根据具体应用场合选择相应的访问方式,而且相对于传统的 关系数据库来说,HBase采用了更加简单的数据模型,把数据存储为未经解释的字符串, 用户...
xsd2pgschema是一个Java应用程序... PgSchema服务器(序列化的PostgreSQL数据模型服务器)可用于加快对复杂XML Schema的分析。 大型XML文件可以通过xmlsplitter拆分,xmlsplitter是基于XPath和StAX的灵活XML拆分器。
节拍模式可以作为对象序列化到本地文件,并使用“保存”和“加载”功能反序列化,允许用户相互发送节拍。 还实施了多线程聊天服务器,以允许通过分享想法来协作创作音乐。 服务器打开一个 ServerSocket 连接并侦听...
泛型的序列化问题 .NET 2.0 泛型在实际开发中的一次小应用 C#2.0 Singleton 的实现 .Net Framwork 强类型设计实践 通过反射调用類的方法,屬性,字段,索引器(2種方法) ASP.NET: State Server Gems 完整的动态加载/卸载...
通过通用数据类型,可以实现对象的序列化,便于各种数据的存储、在网络上的传输。例如,可以不用编写任何代码,即可实现将内存对象保存到XML文件或者数据库,或者从XML文件或数据库恢复内存对象。 强大的管理分析...
CruiseYoung提供的带有详细书签的电子书籍目录 ... 该资料是《C语言入门经典(第4版)》的源代码及... 12.7 格式化文件的输入输出 474 12.7.1 格式化文件输出 474 12.7.2 格式化文件输入 475 12.8 错误处理 477 12.9 ...
Ivor Horton是撰著Java、C和C++编程语言图书的杰出作家之一。大家一致认为,他的著作独具风格,无论是编程新手,还是经验丰富的编程人员,都很容易理解其内容。在个人实践中,Ivor Horton也是一名系统顾问。他从事...
其次,它不仅可以将虚拟地址映射到物理的 RAM 或者 swap,它还可以将它们映射到文件和文件位置,这样,读写内存将对文件中的数据进行读写。不过,在这里,我们只关心 mmap 向进程添加被映射的内存的能力。 munmap() ...