OutOfMemoryError:使用XSLT转换的Java堆空间

时间:2017-10-14 19:08:29

标签: java xml xslt

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

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>

1 个答案:

答案 0 :(得分:2)

通常,XSLT 1.0和2.0使用数据模型,该模型将完整的XML输入提取到树模型中以允许完整的XPath导航,从而导致内存使用量随输入文档的大小而增加。

因此,除非你增加堆空间,如果你当前的文档大小导致内存不足,你可以做多少,至少一般情况下,可能有特定于XSLT处理器和一些XSLT特定的优化,具体取决于你的具体XSLT代码,但你不能避免处理器首先拉入完整的文件。我们需要看到你的XSLT试图判断它是否可以优化。分析样式表有助于识别要优化的区域,我不确定Xalan是否支持这一点。而且我不确定堆栈跟踪是否意味着在为大型输入构建DTM(其树模型)时Xalan已经耗尽内存,在这种情况下显然优化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的beta评估为可流动。