8

我有一个使用 XmlUnit 从两个 XML 文件中获取差异的应用程序。但问题是 XmlUnit 使用 JDOM。我的 xml 文件大约 1GB 大!

将这些 xml 存储在 JDOM 文档中需要太多 RAM。

我曾尝试使用 SlimJDOMFactory 但仍然使用太多 RAM!

实际上我需要在 XML 文件中向前和向后导航。如果没有 JDOM,我没有找到简单的方法。

任何人都可以帮忙吗?

这是关于我如何构建 JDOM 文档的代码示例:

    private org.jdom2.Document refDocJdom2;
    private org.jdom2.Document resDocJdom2;
    SAXBuilder sxb = new SAXBuilder(); 
    sxb.setJDOMFactory(new SlimJDOMFactory());

    popmsg("Validating reference file...");
    try {
        refDocJdom2 = sxb.build(referenceXML_Path); 
    } catch (Exception e) { 
        JOptionPane.showMessageDialog(null, "Error while parsing   Reference : "+referenceXML_Path+" file.\nCheck XML file validity.");
        return;
    }
    popmsg("Reference file validated");

    popmsg("Validating result file....");
    try {
        resDocJdom2 = sxb.build(resultXML_Path); 
    } catch (Exception e) { 
        JOptionPane.showMessageDialog(null, "Error while parsing result "+resultXML_Path+" file.\nCheck XML file validity.");
        return;
    }
    popmsg("Result file validated");
    popmsg("Validation Done.");

    getDifferencies(referenceXML_Path, resultXML_Path);
    d2 = new Date();

  }
public void getDifferencies(String fileRef, String fileRes) throws SAXException, IOException {
    popmsg("Documents : VALID XML format");
    popmsg("Shearching for differencies....");

    Reader refReader;

    refReader = new FileReader(fileRef);
    Reader resReader = new FileReader(fileRes);
    Diff aDifference = new Diff(refReader, resReader);

    if(refReader != null){
        refReader.close();
    }
    refReader = null;

    if(resReader != null){
        resReader.close();
    }
    resReader = null;

    //TODO
     //     XMLUnit.setIgnoreWhitespace(true);

    myDetailledDiff = new DetailedDiff(aDifference);
    myDetailledDiff.overrideDifferenceListener(new IgnoreNamedElementsDifferenceListener());
    myDetailledDiff.overrideElementQualifier(new ElementNameAndAttributeQualifier()); 
    allDiffs = myDetailledDiff.getAllDifferences();
    myDetailledDiff = null;

    popmsg("Got all differencies...\nGoing to Sort them now...");

    popmsg("Diff SIZE : "+allDiffs.size());
    myDiffsList = new ArrayList<MyDifference>(allDiffs.size());
    if(allDiffs.size() > 0){
        Difference aDiff;
        for (int i = 0; i < allDiffs.size(); i++){
            aDiff =  (Difference) allDiffs.get(i);

            myDiffsList.add(new MyDifference(aDiff, refDocJdom2, resDocJdom2));

            if(myDiffsList.size() == LIMIT)
                return ;
            if (i%25 == 0 && i!= 0){
                popmsg("**************************************************\t"+i+"\n");
            }
        }

        allDiffs.clear();
        allDiffs = null;

    }else{
        popmsg("NO DIFERENCIES");
    }
}
4

1 回答 1

10

JDOM 将整个 XML 文档读入内存。这对于任何基于内存的 XML 模型(XOM/DOM/JDOM/等)都是“正常的”。这也是这些系统众所周知的弱点。最终,这个问题没有解决方案,同时仍然保留整个 XML 的内存表示。

读取 XML 文档(通常为 UTF-8)时,磁盘上的 1GB 数据通常按比例转换为内存中的字符数,大约为 2GB。这就是您应该为 1GB XML 文档“预算”的内容。

SlimJDOMFactory 在 XML 中重用字符串,而不是保留对新字符串的引用,本质上它消除了字符串值的重复。当您有许多具有相同名称的元素、标签和其他结构时,这非常方便。例如,如果没有 SlimJDOMFactory,一个具有 1M<tag />个元素的 XML 文档将有 1M 个不同的 Element 实例,每个实例都有自己的 name tag。假设tag大约是一个 32Byte 的对象,那么将需要大约 32MB 来存储这些字符串。SlimJDOMFactory 会将其减少到仅 32Bytes,但是,这只是“到目前为止”,并且它并不能解决随着文档的增长,它将占用更多空间的事实......它只是在您运行时“延迟”记不清。它还有一些其他的后果,无论是好是坏...... 我的测试表明,对于即使在内存中存在几个 GC 周期的文档,内存占用空间较小的净收益很快就会实现,并且解析端的性能成本得到了“回报”。

此问题的典型解决方案是:

  1. 直接使用 SAX,根本没有内存模型....
  2. 将输入文件分成更小的块。这是正常的解决方案,出于多种原因它很有意义(它减少了延迟,您可以并行解析文件等)
  3. 在逻辑上将 XML 拆分为仍然是有效 XML 的部分,并使用文件子集上的特殊 InputStreams 解析文件的部分。
  4. 为您的系统添加更多内存。
  5. 使用自定义 JDOMFactory 跳过您知道永远不需要的内容(JDOMFactory 作为文档 SAXBuild 过程的一部分被调用......因此,您实际上可以将文件内容“修剪”为您知道您将需要的子集...... .... 最终还是会得到一个内存中的 JDOM 文档和可导航的(剩下的)。

这些解决方案都不是“很棒的”,但这就是您使用内存 XML 系统所获得的。

于 2014-05-23T12:33:01.327 回答