java - 内存不足错误: Java heap space using XSLT transform

标签 java xml xslt

我想使用 XSLT 转换 XML 文件。 我做了:

TransformerFactory factory = TransformerFactory.newInstance();
    InputStream is = 
this.getClass().getResourceAsStream(getPathToXSLTFile());
    Source xslt = new StreamSource(is);
    Transformer transformer = factory.newTransformer(xslt);
    Source text = new StreamSource(new File(getInputFileName()));
    transformer.transform(text, new StreamResult(new File(getOutputFileName())));

哪个输入文件有大约 10000000 行,我有错误:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at com.sun.org.apache.xml.internal.utils.FastStringBuffer.append(FastStringBuffer.java:682)
at com.sun.org.apache.xml.internal.dtm.ref.sax2dtm.SAX2DTM.characters(SAX2DTM.java:2111)
at com.sun.org.apache.xalan.internal.xsltc.dom.SAXImpl.characters(SAXImpl.java:863)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.characters(AbstractSAXParser.java:546)
at com.sun.org.apache.xerces.internal.impl.XMLDocumentFragmentScannerImpl.scanDocument(XMLDocumentFragmentScannerImpl.java:455)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:841)
at com.sun.org.apache.xerces.internal.parsers.XML11Configuration.parse(XML11Configuration.java:770)
at com.sun.org.apache.xerces.internal.parsers.XMLParser.parse(XMLParser.java:141)
at com.sun.org.apache.xerces.internal.parsers.AbstractSAXParser.parse(AbstractSAXParser.java:1213)
at com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager.getDTM(XSLTCDTMManager.java:421)
at com.sun.org.apache.xalan.internal.xsltc.dom.XSLTCDTMManager.getDTM(XSLTCDTMManager.java:215)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.getDOM(TransformerImpl.java:556)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:739)
at com.sun.org.apache.xalan.internal.xsltc.trax.TransformerImpl.transform(TransformerImpl.java:351)
at ru.magnit.task.utils.AbstractXmlUtil.transformXML(AbstractXmlUtil.java:66)
at ru.magnit.task.EntryPoint.main(EntryPoint.java:72)

在这一行中:

 transformer.transform(text, new StreamResult(new File(getOutputFileName())));

这样做的原因是什么?可以在没有堆大小的情况下以某种方式对其进行优化吗?

更新: 我的 XSLT 文件:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:output method="xml" indent="yes"/>

<xsl:template match="entries">
    <entries>
        <xsl:apply-templates/>
    </entries>
</xsl:template>

<xsl:template match="entry">
    <entry>
        <xsl:attribute name="field">
            <xsl:apply-templates select="*"/>
        </xsl:attribute>
    </entry>
</xsl:template>

最佳答案

一般来说,XSLT 1.0 和 2.0 使用的数据模型将完整的 XML 输入拉入树模型以允许完整的 XPath 导航,从而导致内存使用量随着输入文档的大小而增加。

因此,除非您增加堆空间,否则如果您当前的文档大小导致内存不足,您无能为力,至少一般而言,可能存在特定于 XSLT 处理器和一些特定于 XSLT 的优化,具体取决于您的具体 XSLT 代码,但您无法避免处理器首先提取完整的文档。我们需要查看您的 XSLT 来尝试判断它是否可以优化。分析样式表可以帮助识别需要优化的区域,我不确定 Xalan 是否支持这一点。我不确定该堆栈跟踪是否仅仅意味着 Xalan 在为大型输入构建 DTM(其树模型)时已经耗尽内存,在这种情况下,显然优化 XSLT 代码没有帮助,因为它甚至没有被执行.

您可以尝试的一种 Java 特定方法是使用 https://docs.oracle.com/javase/8/docs/api/javax/xml/transform/sax/SAXTransformerFactory.html相反,从样式表创建 SAX 过滤器并将其与默认 Transformer 链接以序列化过滤器的结果,我想我曾经尝试过,发现它比使用 Transformer 的传统方法消耗的内存更少。

XSLT 3.0 尝试使用新的流方法 ( https://www.w3.org/TR/xslt-30/#streaming-concepts ) 来解决内存问题,但是到目前为止,只有一种商业产品 Saxon 9 EE 的实现。一般来说,样式表不一定是可流式的,相反,您必须重写它以使其可流式传输(如果可能的话,例如,通过流式传输不可能对输入节点进行排序)。

例如,您发布的样式表转换为 XSLT 3.0 以使用流式传输(无需重写,只需将默认模式设置为可流式传输)

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:math="http://www.w3.org/2005/xpath-functions/math"
    exclude-result-prefixes="xs math"
    version="3.0">

    <xsl:mode streamable="yes"/>

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="entries">
        <entries>
            <xsl:apply-templates/>
        </entries>
    </xsl:template>

    <xsl:template match="entry">
        <entry>
            <xsl:attribute name="field">
                <xsl:apply-templates select="*"/>
            </xsl:attribute>
        </entry>
    </xsl:template>

</xsl:stylesheet>

Saxon 9.8 EE 和 Exselt 测试版将其评估为可流式传输。

关于java - 内存不足错误: Java heap space using XSLT transform,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46748327/

相关文章:

java - 如何使两个单独的 Mac 终端(命令提示符窗口)相互发送短信而不会出现 "Address already in use"错误?

java - 将迭代器传递给方法

c# - 防止名称以<culture>结尾的XML文件生成为资源文件

c# - 如何查找具有给定名称的最高级别后代

跨 OSGI 包的 XSLT 依赖关系

xslt - 在 xslt 2.0 中使用时区将纪元转换为日期和时间

java - Oracle XSLT 处理器

java - Jackson用空字符串而不是对象反序列化JSON字符串

android - 如何在android 2.1版本中重写或修改XML

java - Eclipse Web 项目名称