自从第一个Java版本开始,很多开发人员一直都在尝试让Java获得最少和C/C++一样的表现。JVM提供商尽他们最大的努力去实现一些新的JIT算法,但是还是有很多需要做的,特别是在我们使用Java的方法上。
例如,在对象<->文件序列化上就差距很大--尤其在读写内存对象上。我将就这个主题做一些解释和分享。
所有的测试都是在下面这个对象上执行的:
1
|
public
class
TestObject
implements
Serializable {
|
3
|
private
long
longVariable;
|
4
|
private
long
[] longArray;
|
5
|
private
String stringObject;
|
6
|
private
String secondStringObject;
|
为了简单起见,我将只贴出写入方法(尽管读取类似),完整的源码在我的GitHub上可以找到(http://github.com/jkubrynski/serialization-tests
)
最标准的java序列化(我们都是从这里学起的)是这样的:
01
|
public
void
testWriteBuffered(TestObject test, String fileName)
throws
IOException {
|
02
|
ObjectOutputStream objectOutputStream =
null
;
|
04
|
FileOutputStream fos =
new
FileOutputStream(fileName);
|
05
|
BufferedOutputStream bos =
new
BufferedOutputStream(fos);
|
06
|
objectOutputStream =
new
ObjectOutputStream(bos);
|
07
|
objectOutputStream.writeObject(test);
|
09
|
if
(objectOutputStream !=
null
) {
|
10
|
objectOutputStream.close();
|
提升标准序列化速度的最简单方法时使用RandomAccessFile对象:
01
|
public
void
testWriteBuffered(TestObject test, String fileName)
throws
IOException {
|
02
|
ObjectOutputStream objectOutputStream =
null
;
|
04
|
RandomAccessFile raf =
new
RandomAccessFile(fileName,
"rw"
);
|
05
|
FileOutputStream fos =
new
FileOutputStream(raf.getFD());
|
06
|
objectOutputStream =
new
ObjectOutputStream(fos);
|
07
|
objectOutputStream.writeObject(test);
|
09
|
if
(objectOutputStream !=
null
) {
|
10
|
objectOutputStream.close();
|
更高深点的技术是使用Kryo
框架,新旧版本的差距是很大的,我做过测试。因为性能比较上并没有体现出特别引人注意的差异,所以我将使用2.x版本,因为它对用户更友好而且更快些。
01
|
private
static
Kryo kryo =
new
Kryo();
|
03
|
public
void
testWriteBuffered(TestObject test, String fileName)
throws
IOException {
|
04
|
Output output =
null
;
|
06
|
RandomAccessFile raf =
new
RandomAccessFile(fileName,
"rw"
);
|
07
|
output =
new
Output(
new
FileOutputStream(raf.getFD()), MAX_BUFFER_SIZE);
|
08
|
kryo.writeObject(output, test);
|
10
|
if
(output !=
null
) {
|
最后一个方案是在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
;
|
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) {
|
TestObject写入方法如下:
01
|
public
void
write(MemoryBuffer unsafeBuffer) {
|
02
|
unsafeBuffer.putLong(longVariable);
|
03
|
unsafeBuffer.putLongArray(longArray);
|
05
|
boolean
objectExists = stringObject !=
null
;
|
06
|
unsafeBuffer.putBoolean(objectExists);
|
08
|
unsafeBuffer.putCharArray(stringObject.toCharArray());
|
10
|
objectExists = secondStringObject !=
null
;
|
11
|
unsafeBuffer.putBoolean(objectExists);
|
13
|
unsafeBuffer.putCharArray(secondStringObject.toCharArray());
|
直接内存缓冲区类(已简化了的,仅仅为了展示这个思想)
01
|
public
class
MemoryBuffer {
|
03
|
public
static
final
Unsafe unsafe = UnsafeUtil.getUnsafe();
|
05
|
private
final
byte
[] buffer;
|
07
|
private
static
final
long
byteArrayOffset = unsafe.arrayBaseOffset(
byte
[].
class
);
|
08
|
private
static
final
long
longArrayOffset = unsafe.arrayBaseOffset(
long
[].
class
);
|
几个小时的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获得最少和C/C++一样的表 现。JVM提供商尽他们最大的努力去实现一些新的JIT算法,但是还是有很多需要做的,特别 是在我们使用Java的方法上。
从LCD到RDF 将ILCD XML序列化中的LCA数据转换为(大致等效)RDF / XML序列化ILCD模式非常大,此代码仅转换很小的子集,足以表示进程及其输入和输出流。 这样做的主要动机是探索关联数据和LCA的机会和问题。依存关系...
·使用Java序列化存储版本和提交信息。 ·多线程可加快文件操作。 ·在VCS中提取最新版本。 ·管理分支机构的操作。 ·文件管理以实现最佳存储。 要做的工作使用cmd版本在Java swing中使其成为可用的GUI
包括支持Java语言中的基本类型、违例处理等,支持java.lang包和java.util包中的绝大部分类和接口,支持正则表达式和序列化。 2. 跨平台支持 如果你使用GWT中提供的显示组件(比如Button)和组装组件(比如VerticalPanel...
HBase可以支持N ative Java API、HBase Shell等多种访问接口,可以根据具体应用场合选择相应的访问方式,而且相对于传统的 关系数据库来说,HBase采用了更加简单的数据模型,把数据存储为未经解释的字符串, 用户...
节拍模式可以作为对象序列化到本地文件,并使用“保存”和“加载”功能反序列化,允许用户相互发送节拍。 还实施了多线程聊天服务器,以允许通过分享想法来协作创作音乐。 服务器打开一个 ServerSocket 连接并侦听...
xsd2pgschema是一个Java应用程序... PgSchema服务器(序列化的PostgreSQL数据模型服务器)可用于加快对复杂XML Schema的分析。 大型XML文件可以通过xmlsplitter拆分,xmlsplitter是基于XPath和StAX的灵活XML拆分器。
通过通用数据类型,可以实现对象的序列化,便于各种数据的存储、在网络上的传输。例如,可以不用编写任何代码,即可实现将内存对象保存到XML文件或者数据库,或者从XML文件或数据库恢复内存对象。 强大的管理分析...
泛型的序列化问题 .NET 2.0 泛型在实际开发中的一次小应用 C#2.0 Singleton 的实现 .Net Framwork 强类型设计实践 通过反射调用類的方法,屬性,字段,索引器(2種方法) ASP.NET: State Server Gems 完整的动态加载/卸载...
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() ...