use*_*356 6 java memory xml-parsing
我们的应用程序需要以XML格式(几个文件)呈现客户端数据,并将其解析为我们的通用XML格式(带有模式的单个文件).为此,我们使用apache的XMLBeans数据绑定框架.下面简要描述该过程的步骤.
首先,我们将原始的java.io.File对象指向磁盘上的客户端XML文件,并将它们加载到一个集合中.然后,我们遍历此集合,为每个文件创建一个apache.xmlbeans.XmlObject.在将所有文件解析为XmlObjects之后,我们创建了4个集合,其中包含我们感兴趣的XML文档中的各个对象(要清楚,这些不是手工制作的对象,而是我只能描述为创建的'代理'对象通过apache的XMLBeans框架).最后一步,我们迭代这些集合以生成我们的 XML文档(在内存中),然后将其保存到磁盘.
对于大多数用例,此过程运行正常,并且在给定'-Xmx1500m'命令行参数时可以在JVM中轻松运行.但是,当我们为客户提供"大数据集"时会出现问题.在这种情况下,大型的123Mb客户端XML分布在7个文件中.这样的数据集导致我们的代码内集合中填充了大约40,000个上述"代理对象".在这些情况下,内存使用只是通过屋顶.我没有得到任何outofmemory异常程序只是挂起,直到垃圾收集发生,释放少量内存,然后程序继续,耗尽这个新空间,循环重复.这些解析会话目前需要4-5个小时.我们的目标是在一小时内将其降低.
重要的是要注意将客户端xml转换为xml所需的计算需要所有xml数据进行交叉引用.因此,我们无法实现顺序解析模型或将此流程批处理为较小的块.
到目前为止我尝试过的
不是在内存中保存所有123Mb的客户端xml,而是在每次数据请求时,加载文件,查找数据并释放对这些对象的引用.这似乎可以减少在此过程中消耗的内存量,但是您可以想象,常量I/O所花费的时间消除了减少内存占用的好处.
我怀疑一个问题是我们持有一个价值123Mb的XML文件的XmlObject []以及从这些文件中获取的对象集合(使用xpath查询).为了解决这个问题,我改变了逻辑,以便不是查询这些集合,而是直接查询文档.这里的想法是,在任何时候都不存在4个大量的列表,其中包含10个1000的对象,只是XmlObjects的大集合.这似乎没有任何区别,在某些情况下,甚至会增加内存占用.
现在抓住吸管,我认为在写入磁盘之前我们用来构建我们的 xml内存的XmlObject 变得太大而无法与所有客户端数据一起维护.但是,对此对象执行一些sizeOf查询显示,在此最大的对象上,此对象小于10Kb.在阅读了XmlBeans如何管理大型DOM对象之后,它似乎使用了某种形式的缓冲编写器,因此很好地管理了这个对象.
所以现在我没有想法; 不能使用SAX方法而不是内存密集型DOM方法,因为我们在任何时候都需要100%的客户端数据,在我们绝对需要它之前不能阻止请求这些数据,因为转换过程需要大量的循环和磁盘I/O时间不值得保存的内存空间,我似乎无法以减少内部java集合占用的空间量的方式构造我们的逻辑.我在这里运气不好吗?我必须接受,如果我想将123Mb的xml数据解析成我们的Xml格式,我无法用1500m的内存分配吗?虽然123Mb是我们域中的一个大型数据集,但我无法想象其他人从来没有必须同时使用Gb的数据做类似的事情.
其他可能很重要的信息

我采取的方法是对文件进行两次传递,在两种情况下都使用 SAX。
第一遍会将计算中所需的“交叉引用”数据解析为自定义对象并将其存储Map。如果“交叉引用”数据很大,那么考虑使用分布式缓存(如果您从Maps 开始,那么一致性是最合适的)。
第二遍将解析文件,检索“交叉引用”数据以根据需要执行计算,然后使用 API 写入输出 XML javax.xml.stream。