Java 使用有限的内存构建和导出非常大的树结构

Java 使用有限的内存构建和导出非常大的树结构,java,tree,Java,Tree,总之,我有一个应用程序,它获取一组输入文件,从数据生成一棵树,然后将其作为XML写入文本文件 目前,整个树在写入之前存储在内存中,因为在解析过程中,我们需要引用树上的任意节点来获取或更新其值 当树变得太大而无法将其全部存储在内存中时,就会出现我们面临的问题。这棵树本身非常平坦,只有4-6层的深度。看起来像这样 Root Group Record Data Data Record Data Dat

总之,我有一个应用程序,它获取一组输入文件,从数据生成一棵树,然后将其作为XML写入文本文件

目前,整个树在写入之前存储在内存中,因为在解析过程中,我们需要引用树上的任意节点来获取或更新其值

当树变得太大而无法将其全部存储在内存中时,就会出现我们面临的问题。这棵树本身非常平坦,只有4-6层的深度。看起来像这样

Root
   Group
      Record
         Data
         Data
      Record
         Data
         Data
         Data
         ...
      ...
   Group
      Record
      ...
始终会有一个
根节点,每个节点只有一种类型的子节点。但是,将节点添加到其他节点的方式也没有顺序:根据数据的格式,可以将记录添加到不同的组,也可以将数据添加到不同的记录(而不是为一个组构建一个记录,然后移动到另一个组)

我的第一个建议是给我们的机器增加内存。我们在64位windows机器上运行该工具,因此如果内存不足,那么我们只需要获得更多内存。但这个建议没有被采纳

我的下一个想法是,每当树在内存中占用太多空间时,就写出节点,但由于数据可以随时添加到特定记录中,因此很难确定何时实际完成了记录的处理。特别是如果我们需要查阅一份记录,而它已经被写出来了


还有其他几个选项,例如优化树的设计方式(因为每个节点都占用相当大的内存),但对于这个问题,我想了解构建和导出大型树的技术。

如果我是你,我会在处理树时保留树,而不是将其存储在内存中。所谓持久性,我指的是任何东西,从平面文本文件到关系数据库,再到面向图形的NoSQL解决方案

然后,您可以以JavaBean的形式为需要执行的CRUD操作创建一个基本访问层,现在开始


另一种选择是动态构建XML文件,从而跳过在内存中构建树。为此,您可以利用SAX,它是一种基于事件的XML处理解决方案,而不是将整个文档加载到内存中。

我认为有两种方法可以解决这个问题

  • 了解更多关于使用数据来确定特定模式的信息,并为您的用例提供最有效的方法

  • 将数据视为一个黑匣子,假设访问或更改数据的任何场景都可能以相同的频率和概率出现

对于第一种方法,你没有给我们任何食物,所以我们必须假设后者。缓存的概念作为一种解决方案浮现在脑海中。缓存有不同的类型,但其基本概念是尽可能多地保留在内存中,一旦超过某个限制,就会从内存中保留并删除使用次数最少或最长的部分

执行此操作时,您可以选择在内存中保留实际的树结构,只清除节点内容,或者同时清除节点内容和树结构本身。如果您有大量但数量有限的节点,那么最好保持树结构,使“清除”的节点尽可能轻量级。但是,如果树中节点的数量实际上是无限的,那么您可以考虑清除整个子树。 最后一种方法对于通常通过访问子树(而不是完全随机)进行树访问的用例非常有效

如果您提供有关数据和使用模式的更多信息,我们可能会提出更详细的建议。

1)我想到的第一个选项是将所有(父-子)对存储在数据库中,然后递归地对其进行探索,以从中构建XML

2) 另一种方法是自下而上,扫描完整的输入三次(每层扫描一次,从父记录开始,以根记录结束)。每个层作为一组XML文件存储在磁盘中,每个节点一个。然后,在树中构建更高级别时,子文件可以简单地附加到其相应的父文件中(因为它们保证完全填充)。这需要维护2个内存索引;一个用于当前级别,一个用于其下的级别。这些索引指向这些文件

  • 在磁盘上创建一个工作目录

  • 通读数据。当您遇到以前从未见过的组时,请在工作目录中为其创建一个子目录。当您看到以前从未见过的记录时,请在相应的组目录中为其创建一个文件。当您看到一些数据时,请将其附加到相应的文件中。继续,直到你读完数据

  • 遍历文件树,将记录文件的内容连接到输出文件,并为根文件和组添加适当的标记

  • 删除工作目录及其所有内容

  • 如果您保留内存中的文件句柄,并在写入和读取阶段之间重用它们(这意味着使用
    RandomAccessFile
    MappedByteBuffer
    ,两者都可以写入、读取和回放),并且在任何时候都不刷新它们,然后,您将把磁盘IO和缓存的问题完全交给操作系统(以及运行时库等)。如果操作系统决定执行这个特定的程序最好的方法是将一些数据写入磁盘,它就会这样做。如果它能全部放在内存中,它将把它全部保存在内存中。它将能够批处理写操作,使其美观、大,从而提高效率。它将能够预fetc