【问题标题】:How to test limitation of memory when parsing XML files解析 XML 文件时如何测试内存限制
【发布时间】:2018-02-27 16:50:53
【问题描述】:

我正试图跑到OutOfMemoryException。我的方法创建一个文件,解析如果没有错误然后立即删除该文件,清除垃圾收集并生成一个更大的文件并重复。但是大文件会消耗太多时间和 CPU。有没有更好的方法来做到这一点?谢谢。

 public static void main(String[] args) {
    for (int i = 6000000; i <= 10000000; i+=100000) {
        WriteXml(i);
        try {
            File fXmlFile = new File("limit.xml");
            DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
            new Thread() {
                public void run() {
                    try {
                        dBuilder.parse(fXmlFile);
                    } catch (SAXException | IOException e) {
                        e.printStackTrace();
                    }
                }
            fXmlFile.delete();
            };
        } catch (Exception  e) {
            e.printStackTrace();
            System.out.println(i);
        }
        System.gc();
    }
}

【问题讨论】:

  • 那么你到底在问什么?如何以更节省内存的方式解析 XML?使用 SAX 或 StAX 解析器,而不是 DOM。或者如何检查内存消耗?为此使用分析器。
  • ... 为什么要引起OutOfMemoryException
  • 我试图弄清楚 XML 文件有多大会导致内存不足异常
  • 我正在做一项评估解析器的作业,其中一个要求是找出会导致内存不足的 XML 有多大。
  • 那么,如果你调用dBuilder.parse时你的xml文件太大,它应该抛出一个OutOfMemoryException

标签: java xml parsing memory out-of-memory


【解决方案1】:

我怀疑(根据我的观察),如果您尝试编写一个非常扁平的树(例如,10m 个元素作为根的子节点),那么您在添加新的兄弟节点时会达到 O(n^2) 的性能一个很长的列表,在你耗尽内存之前你已经没有时间(或耐心)了。

我使用 Saxon API 编写了一个小测试,因此我可以尝试使用不同的树模型(也许您可以使用相同的想法):

    public void testDomSizeLimits() {
        try {
            for (int i=1; i<Integer.MAX_VALUE; i*=2) {
                System.err.println("Trying size " + i);
                Configuration config = new Configuration();
// Change the next line depending on the chosen tree model
                TinyBuilder writer = new TinyBuilder(config.makePipelineConfiguration());
                Location loc = ExplicitLocation.UNKNOWN_LOCATION;
                writer.open();
                writer.startDocument(0);
                writer.startElement(new NoNamespaceName("doc"), Untyped.getInstance(), loc, 0);
                for (int j=0; j<i; j++) {
                    writer.startElement(new NoNamespaceName("elem"), Untyped.getInstance(), loc, 0);
                    writer.characters("The quick brown fox", loc, 0);
                    writer.endElement();
                }
                writer.endDocument();
                writer.close();
            }
        } catch (XPathException e) {
            e.printStackTrace();
        }
    }

在大约 1600 万条记录之后,DOM 和 JDOM2 都变得异常缓慢。但是,Saxon 的 TinyTree 会一直运行到内存不足:

Trying size 1
Trying size 2
Trying size 4
Trying size 8
Trying size 16
Trying size 32
Trying size 64
Trying size 128
Trying size 256
Trying size 512
Trying size 1024
Trying size 2048
Trying size 4096
Trying size 8192
Trying size 16384
Trying size 32768
Trying size 65536
Trying size 131072
Trying size 262144
Trying size 524288
Trying size 1048576
Trying size 2097152
Trying size 4194304
Trying size 8388608
Trying size 16777216
Trying size 33554432
Trying size 67108864

java.lang.OutOfMemoryError: Java heap space

    at java.util.Arrays.copyOf(Arrays.java:3284)
    at net.sf.saxon.tree.tiny.TinyTree.ensureNodeCapacity(TinyTree.java:233)
    at net.sf.saxon.tree.tiny.TinyTree.addNode(TinyTree.java:345)
    at net.sf.saxon.tree.tiny.TinyBuilder.makeTextNode(TinyBuilder.java:405)
    at net.sf.saxon.tree.tiny.TinyBuilder.characters(TinyBuilder.java:381)
    at jaxptest.DOMTest.testDomSizeLimits(DOMTest.java:1424)

这是在 IntelliJ 下使用默认堆大小运行的。

随着节点数量的增加,更合理的测试可能会增加树的深度。今天没时间尝试。

【讨论】:

  • 谢谢,Michael,当我下载 Saxon API 并使用您的代码时,我的机器可以解析多达 3300 万个元素。但是当我使用 JMH 时,它的内存不足 600 万个元素。你知道这是为什么吗?
  • 抱歉,我没有足够的 JMH 经验来测试。显然,您需要确保两个环境都分配了相同数量的内存,以使结果具有可比性。但 JMH 可能会使用额外的资源进行检测。
【解决方案2】:

以字节为单位的原始文件大小并不是导致OutOfMemoryException 的唯一因素;标记的数量和性质也可能有所贡献。因此,您可能希望您的测试包含以下可能性的某种组合:

  • 低标记测试: 创建一个包含单个根元素的 XML 文件,并改变该元素中的文本数量 $t:&lt;r&gt;$t&lt;/r&gt; for $t 具有 100M、1G、10G、100G 等长度。
  • 高标记广度测试: 创建一个 XML 文件,其中一些标记 $b 在根元素中多次出现:&lt;r&gt;$b&lt;/r&gt; for $b 重复的次数越来越多。
  • 高标记深度测试: 创建一个 XML 文件,其中一些标记 $d 从根元素递归多次出现:$d = &lt;r&gt;$d&lt;/r&gt; for $d 递归的次数越来越多。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-18
    • 1970-01-01
    相关资源
    最近更新 更多