我们的应用程序需要处理以XML格式(多个文件)呈现的客户端数据,并将其解析成我们的通用XML格式(具有模式的单个文件)。为此,我们使用了Apache的XMLBeans数据绑定框架。这个过程的步骤如下:
首先,我们获取指向磁盘上客户端XML文件的原始java.io.File对象并将它们加载到集合中。然后,我们遍历该集合,为每个文件创建一个apache.xmlbeans.XmlObject。在所有文件都被解析为XmlObjects之后,我们创建4个集合,分别保存我们感兴趣的XML文档中的各个对象(请注意,这些不是手工制作的对象,而是由Apache的XMLBeans框架创建的“代理”对象)。最后,我们遍历这些集合以生成我们的XML文档(在内存中),然后将其保存到磁盘。
对于大多数用例,这个过程运行良好,并且可以轻松地在JVM中运行,只需给出“-Xmx1500m”命令行参数即可。但是,当客户端提供“大型数据集”时,问题就会出现。这里的大型数据集是指分布在7个文件中的123Mb的客户端XML。这样的数据集导致我们的代码集合中填充了近40,000个前面提到的“代理对象”。在这些情况下,内存使用量会飙升。我没有收到任何内存不足的异常,程序只是挂起,直到垃圾回收发生,释放一小部分内存,然后程序继续运行,使用这个新的空间,循环重复。这些解析会话目前需要4-5个小时。我们的目标是将其缩短到1个小时以内。
需要注意的是,将客户端XML转换为我们的XML所需的计算需要所有XML数据进行交叉引用。因此,我们无法实现顺序解析模型或将此过程批处理成较小的块。
我尝试了以下方法:
在每次请求数据时,而不是将所有123Mb的客户端XML保存在内存中,加载文件,查找数据并释放对这些对象的引用。这似乎减少了在过程中消耗的内存量,但是正如您可以想象的那样,持续的I/O操作所需的时间抵消了减少的内存占用的好处。
我怀疑问题在于我们正在保存123Mb的XML文件的XmlObject[]以及从这些文档中获取的对象集合(使用xpath查询)。为了解决这个问题,我改变了逻辑,使得不再查询这些集合,而是直接查询文档。这里的想法是,在任何时候都不存在大量包含成千上万个对象的4个大型列表,只有大型的XmlObjects集合。但是,这似乎没有任何影响,并且在某些情况下,甚至增加了内存占用。
现在我只是在抓住最后的稻草,考虑到我们在内存中构建xml之前使用的XmlObject过于庞大,无法与所有客户端数据一起维护。但是,对这个对象进行了一些sizeOf查询后发现,在其最大的情况下,该对象不到10Kb。在阅读有关XmlBeans如何管理大型DOM对象的文章后,它似乎使用某种缓冲写入器,并因此很好地管理了这个对象。
所以现在我已经没有什么主意了;不能使用SAX方法代替内存密集的DOM方法,因为我们需要100%的客户端数据在我们的应用程序中任何时候,不能推迟请求这些数据,直到我们绝对需要它,因为转换过程需要大量循环,磁盘I / O时间不值得节省的内存空间,并且我似乎无法以减少内部java集合所占用的空间的方式来构造我们的逻辑。我在这里没戏了吗?如果我想将123Mb的xml数据解析成我们的Xml格式,那么我是否必须接受1500m内存分配不足的事实?虽然在我们的领域中,123Mb是一个大型数据集,但我无法想象其他人从未不得不同时处理Gb的数据。
其他可能重要的信息:
- 我已经使用JProbe尝试查看它是否可以告诉我任何有用的信息。虽然我是一个分析新手,但我运行了他们关于内存泄漏和线程锁定的教程,理解了它们,并且我们的代码中似乎没有任何泄漏或瓶颈。在使用大型数据集运行应用程序后,我们很快就会在内存分析屏幕上看到“锯齿形”类型的形状(请参见附图),其中PS Eden空间被一个巨大的PS Old Gen绿色块所占据。这导致我相信,问题在于对象集合所占用的空间量而不是泄漏保留未使用的内存。 - 我正在运行64位Windows 7平台,但这将需要在32位环境中运行。
首先,我们获取指向磁盘上客户端XML文件的原始java.io.File对象并将它们加载到集合中。然后,我们遍历该集合,为每个文件创建一个apache.xmlbeans.XmlObject。在所有文件都被解析为XmlObjects之后,我们创建4个集合,分别保存我们感兴趣的XML文档中的各个对象(请注意,这些不是手工制作的对象,而是由Apache的XMLBeans框架创建的“代理”对象)。最后,我们遍历这些集合以生成我们的XML文档(在内存中),然后将其保存到磁盘。
对于大多数用例,这个过程运行良好,并且可以轻松地在JVM中运行,只需给出“-Xmx1500m”命令行参数即可。但是,当客户端提供“大型数据集”时,问题就会出现。这里的大型数据集是指分布在7个文件中的123Mb的客户端XML。这样的数据集导致我们的代码集合中填充了近40,000个前面提到的“代理对象”。在这些情况下,内存使用量会飙升。我没有收到任何内存不足的异常,程序只是挂起,直到垃圾回收发生,释放一小部分内存,然后程序继续运行,使用这个新的空间,循环重复。这些解析会话目前需要4-5个小时。我们的目标是将其缩短到1个小时以内。
需要注意的是,将客户端XML转换为我们的XML所需的计算需要所有XML数据进行交叉引用。因此,我们无法实现顺序解析模型或将此过程批处理成较小的块。
我尝试了以下方法:
在每次请求数据时,而不是将所有123Mb的客户端XML保存在内存中,加载文件,查找数据并释放对这些对象的引用。这似乎减少了在过程中消耗的内存量,但是正如您可以想象的那样,持续的I/O操作所需的时间抵消了减少的内存占用的好处。
我怀疑问题在于我们正在保存123Mb的XML文件的XmlObject[]以及从这些文档中获取的对象集合(使用xpath查询)。为了解决这个问题,我改变了逻辑,使得不再查询这些集合,而是直接查询文档。这里的想法是,在任何时候都不存在大量包含成千上万个对象的4个大型列表,只有大型的XmlObjects集合。但是,这似乎没有任何影响,并且在某些情况下,甚至增加了内存占用。
现在我只是在抓住最后的稻草,考虑到我们在内存中构建xml之前使用的XmlObject过于庞大,无法与所有客户端数据一起维护。但是,对这个对象进行了一些sizeOf查询后发现,在其最大的情况下,该对象不到10Kb。在阅读有关XmlBeans如何管理大型DOM对象的文章后,它似乎使用某种缓冲写入器,并因此很好地管理了这个对象。
所以现在我已经没有什么主意了;不能使用SAX方法代替内存密集的DOM方法,因为我们需要100%的客户端数据在我们的应用程序中任何时候,不能推迟请求这些数据,直到我们绝对需要它,因为转换过程需要大量循环,磁盘I / O时间不值得节省的内存空间,并且我似乎无法以减少内部java集合所占用的空间的方式来构造我们的逻辑。我在这里没戏了吗?如果我想将123Mb的xml数据解析成我们的Xml格式,那么我是否必须接受1500m内存分配不足的事实?虽然在我们的领域中,123Mb是一个大型数据集,但我无法想象其他人从未不得不同时处理Gb的数据。
其他可能重要的信息:
- 我已经使用JProbe尝试查看它是否可以告诉我任何有用的信息。虽然我是一个分析新手,但我运行了他们关于内存泄漏和线程锁定的教程,理解了它们,并且我们的代码中似乎没有任何泄漏或瓶颈。在使用大型数据集运行应用程序后,我们很快就会在内存分析屏幕上看到“锯齿形”类型的形状(请参见附图),其中PS Eden空间被一个巨大的PS Old Gen绿色块所占据。这导致我相信,问题在于对象集合所占用的空间量而不是泄漏保留未使用的内存。 - 我正在运行64位Windows 7平台,但这将需要在32位环境中运行。