Java对象序列化性能提示

我必须将一个巨大的对象树(7,000)序列化到磁盘中。 最初我们将这棵树保存在一个带有Kodo的数据库中,但它会使成千上万的查询将这棵树加载到内存中,并且它将占用本地宇宙可用时间的很大一部分。

我为此尝试了序列化,实际上我获得了性能提升。 但是,我觉得我可以通过编写自己的自定义序列化代码来改进这一点。 我需要尽快加载这个序列化对象。

在我的机器中,序列化/反序列化这些对象大约需要15秒。 从数据库加载它们大约需要40秒。

关于我可以做些什么来改善这种性能的任何提示,考虑到因为对象在树中,它们相互引用?

一种优化是自定义类描述符,以便将类描述符存储在不同的数据库中,而在对象流中只能通过ID引用它们。 这减少了序列化数据所需的空间。 例如,请参阅一个项目中的SerialUtil和ClassesTable类如何执行此操作。

使类Externalizable而不是Serializable可以带来一些性能优势。 缺点是它需要大量的手工工作。

然后还有其他序列化库,例如jserial ,它可以提供比Java的默认序列化更好的性能。 此外,如果对象图不包含循环,那么它可以更快地序列化,因为序列化程序不需要跟踪它看到的对象(参见jserial的FAQ中的 “它是如何工作的?”)。

我建议你实现自定义writeObject()readObject()方法。 通过这种方式,您将能够为树中的每个节点释放写入的chidren节点。 使用默认序列化时,每个节点都将与其所有子节点序列化。

例如, Tree类的writeObject()应该遍历的所有节点,并且只使用一些标记来编写节点数据(没有节点本身),这些标记用于标识树级别。

您可以查看LinkedList ,了解这些方法在那里的实现方式。 它使用相同的方法来防止每个单个条目的写入prev和下一个条目。

为避免编写自己的序列化代码,请尝试使用Google Protocol Buffers 。 根据他们的网站:

协议缓冲区是Google的语言中立,平台中立,可扩展的机制,用于序列化结构化数据 – 思考XML,但更小,更快,更简单。 您可以定义数据的结构化时间,然后您可以使用特殊生成的源代码轻松地在各种数据流中编写和读取结构化数据,并使用各种语言 – Java,C ++或Python

我没有使用它,但已经听到很多关于它的积极的事情。 另外,我必须维护一些自定义序列化代码,这可能是一个绝对的噩梦(更不用说追踪错误),所以让别人为你做这件事总是好事。

您是否尝试过压缩流(GZIPOutputStream)?

这就是我要做的,成为我的头脑

序列化

  1. 单独序列化每个对象
  2. 为每个对象分配一个唯一键
  3. 当对象保存对另一个对象的引用时,将该对象的唯一键放在序列化中的对象中。 (我会使用转换为二进制的UUID)
  4. 使用唯一键将每个对象保存到文件/数据库/存储中

反序列化

  1. 从一个任意对象(通常是我怀疑的根)开始,将它反序列化并将其放在一个映射中,并将其唯一键作为索引并返回
  2. 当你在序列化流中踩到一个对象键时, 首先通过在地图中查找它的唯一键来检查它是否已被反序列化 ,如果它只是从那里抓取它,如果没有放入一个延迟加载代理(重复这两个步骤)对于那个对象)而不是具有钩子的真实对象,以便在需要时加载正确的对象。

编辑 ,你可能需要使用两遍序列化和反序列化,如果你有循环引用,它会使事情变得复杂 – 但不是那么多。

为了性能,我建议不要使用java.io序列化。 而是自己深入了解字节。

如果你要使用java.io序列化树,你可能需要确保你的递归不会太深,无论是通过展平(如TreeSet那样)还是先安排序列化最深的节点(所以你要反向引用比嵌套的readObject调用)。

如果在Kodo中没有办法在一个(或几个)中读取整个树,我会感到惊讶。

另外,看看XStream ,一个将对象序列化为XML并再次返回的库。

您可以使用Colfer生成bean,Java的标准序列化性能将获得10 – 1000x的提升。 除非尺寸超过GB,否则你将远远低于一秒。