我有一个Java程序,它将数据准备到内存中相当复杂的大数据结构(几GB)并将其序列化到磁盘,另一个程序读取内存中的序列化数据结构。我惊讶地注意到反序列化步骤非常慢,并且它受CPU限制。 (top
中100%的CPU使用率,但iotop
仅读取3到5 MB / s,这对于硬盘驱动器上的顺序读取来说非常低。 CPU是最新的(Core i7-3820),结构适合内存,没有配置交换空间。
为什么会这样?是否有另一种方法可以在Java中序列化没有CPU作为瓶颈的对象?
以下是反序列化代码,如果重要的话:
FileInputStream f = new FileInputStream(path);
ObjectInputStream of = new ObjectInputStream(f);
Object obj = of.readObject();
答案 0 :(得分:4)
反序列化非常昂贵。如果使用通用反序列化,它将使用大量反射和对象创建。
有很多替代品更快,大多数使用生成的代码而不是反射。
http://code.google.com/p/thrift-protobuf-compare/wiki/Benchmarking
您会注意到其中一个最快的是使用Externalizable,这可能是您的选择。这意味着为对象的序列化和反序列化添加自定义方法。
我编写了最快速的方法,但这可以避免通过回收它们或在原地使用文件中的数据来创建任何对象(即无需反序列化它们)
答案 1 :(得分:2)
很难说没有用剖析器查看这个或者对对象结构的实际层次结构了解很多,但我假设如果它“相当复杂”并且在“几GB”大的顺序上,你可能会处理数以千计的个别物品。
我最好的猜测是你的表现被Java Reflection杀死了。反射用于构造流中的对象,已知它比在代码中直接调用构造函数至少慢两个数量级。因此,如果你的对象有很多“小”对象,那么Reflection将花费大量时间来重构它们。
您可以尝试的一件事(如果您还没有)将在每个Serializable
类的顶部声明以下行:
private static final long serialVersionUID = [some number]L;
如果你没有声明这个ID,Java将不得不计算它,所以你通过声明它来节省一些CPU周期。
供进一步参考: