【问题标题】:XOM canonicalization takes too longXOM 规范化耗时过长
【发布时间】:2012-11-25 15:44:08
【问题描述】:

我有一个 1GB 大的 XML 文件。我正在使用 XOM 来避免 OutOfMemory 异常。

我需要规范化整个文档,但规范化需要很长时间,即使是 1.5 MB 的文件也是如此。

这是我所做的:

我有这个示例 XML 文件,我通过复制 Item 节点来增加文档的大小。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<Packet id="some" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Head>
<PacketId>a34567890</PacketId>
<PacketHeadItem1>12345</PacketHeadItem1>
<PacketHeadItem2>1</PacketHeadItem2>
<PacketHeadItem3>18</PacketHeadItem3>
<PacketHeadItem4/>
<PacketHeadItem5>12082011111408</PacketHeadItem5>
<PacketHeadItem6>1</PacketHeadItem6>
</Head>
<List id="list">
    <Item>
        <Item1>item1</Item1>
        <Item2>item2</Item2>
        <Item3>item3</Item3>
        <Item4>item4</Item4>
        <Item5>item5</Item5>
        <Item6>item6</Item6>
        <Item7>item7</Item7>
    </Item>
</List>
</Packet>

我用于规范化的代码如下:

private static void canonXOM() throws Exception {
    String file = "D:\\PACKET.xml";
    FileInputStream xmlFile = new FileInputStream(file);

    Builder builder = new Builder(false);
    Document doc = builder.build(xmlFile);

    FileOutputStream fos = new FileOutputStream("D:\\canon.xml");
    Canonicalizer outputter = new Canonicalizer(fos);

    System.out.println("Query");
    Nodes nodes = doc.getRootElement().query("./descendant-or-self::node()|./@*");

    System.out.println("Canon");
    outputter.write(nodes);

    fos.close();
}

尽管此代码适用于小文件,但在我的开发环境(4gb ram、64bit、eclipse、windows)上,规范化部分需要大约 7 分钟才能处理 1.5mb 文件

非常感谢任何指向此延迟原因的指针。

PS。我需要规范化整个 XML 文档以及整个文档本身的片段。因此,使用文档本身作为参数对我不起作用。

最好的

【问题讨论】:

  • 您的原始 XML 文件对我来说看起来很规范。有什么不规范的?

标签: java xml performance canonicalization xom


【解决方案1】:

memory is not restriction

main thread is green and no blocking. it is using as much cpu as it can. 
because my machine has multi-cores , so the CPU total usage is not full.
But it will be full for a single CPU the main thread is running on.

Nodes.contains is the most busy one

内部节点在 List 中进行管理,并进行线性比较。 列表中的项目越多,“包含”会变慢。

private final List nodes;
public boolean contains(Node node) {
    return nodes.contains(node);
}

所以

  • 尝试修改 lib 的代码以使用 HashMap 来保存节点。
  • 或使用多线程来利用更多 CPU,前提是您的 XML 可以拆分为小 xml。

工具:JVisualVM。 http://docs.oracle.com/javase/6/docs/technotes/guides/visualvm/index.html

【讨论】:

  • 感谢您的回答。很有见地。您认为有更有效的方法来规范化节点吗?
  • 不确定。如果您仍然想使用这个库,也许您可​​以尝试通过使用 HashMap 来保存节点来修改库的代码。或尝试其他一些库或方法。 :)
  • 如果您的 XML 可以拆分为小的 XML,那么您可以使用多线程来利用更多的 CPU。因为目前您的应用程序已经接近 100% 在一个 CPU 中。使用更多 CPU 显然可以减少应用的运行时间。
【解决方案2】:

既然你想序列化整个文档,你可以直接替换

Nodes nodes = doc.getRootElement().query("./descendant-or-self::node()|./@*");
outputter.write(nodes);

outputter.write(doc);

?

看起来Canonicalizer 做了额外的工作(例如 whunmr 提到的nodes.contains() 调用)当给定一个节点列表而不是一个根节点来规范化时。

如果这不起作用或还不够,我会 fork Canonicalizer 并按照分析的建议在那里进行优化。

【讨论】:

  • 我实际上需要规范化 XML 文档中的段。我意识到这个例子有点误导。我将不得不规范化 List 节点。所以我不一定要规范整个文档。
【解决方案3】:

如果您愿意放弃 XOM,我可能会解决您的问题。我的解决方案包括使用XPath APIApache Santuario

性能上的差异令人印象深刻,但我认为提供一个比较会很好。

对于测试,我使用了您在问题中提供的 XML 文件,大小为 1.5MB。

XOM 测试

FileInputStream xmlFile = new FileInputStream("input.xml");

Builder builder = new Builder(false);
Document doc = builder.build(xmlFile);

FileOutputStream fos = new FileOutputStream("output.xml");
nu.xom.canonical.Canonicalizer outputter = new nu.xom.canonical.Canonicalizer(fos);

Nodes nodes = doc.getRootElement().query("./descendant-or-self::node()|./@*");
outputter.write(nodes);

fos.close();

XPath/Santuario 测试

org.apache.xml.security.Init.init();

DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance();
domFactory.setNamespaceAware(true);
DocumentBuilder builder = domFactory.newDocumentBuilder();
org.w3c.dom.Document doc = builder.parse("input.xml");

XPathFactory xpathFactory = XPathFactory.newInstance();
XPath xpath = xpathFactory.newXPath();

org.w3c.dom.NodeList result = (org.w3c.dom.NodeList) xpath.evaluate("./descendant-or-self::node()|./@*", doc, XPathConstants.NODESET);

Canonicalizer canon = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_OMIT_COMMENTS);
byte canonXmlBytes[] = canon.canonicalizeXPathNodeSet(result);

IOUtils.write(canonXmlBytes, new FileOutputStream(new File("output.xml")));

结果

下表以秒为单位显示结果。测试进行了 16 次。

╔═════════════════╦═════════╦═══════════╗
║      Test       ║ Average ║ Std. Dev. ║
╠═════════════════╬═════════╬═══════════╣
║ XOM             ║ 140.433 ║   4.851   ║
╠═════════════════╬═════════╬═══════════╣
║ XPath/Santuario ║ 2.4585  ║  0.11187  ║
╚═════════════════╩═════════╩═══════════╝

性能差异巨大,与XML Path Language的实现有关。使用 XPath/Santuario 的缺点是它们不像 XOM 那样简单。

测试详情

机器:Intel Core i5 4GB RAM
SO:Debian 6.0 64位
Java:OpenJDK 1.6.0_18 64bit
XOM:1.2.8
Apache Santuario:1.5.3

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-16
    • 2013-02-26
    • 2011-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多