文件集耗尽内存/没有释放内存的Ant XSLT任务

时间:2011-04-27 15:03:38

标签: xslt ant out-of-memory saxon

我有一个很大的(1.9 GB)XML文件,它每个月都有我要插入MySQL数据库的数据。我为此制作了一个Ant脚本。

Ant XSLT任务无法处理这么大的一个文件,因此我有一个使用xml_split(来自xml-twig-tools)的任务将1.9 GB xml文件拆分为大约4 MB的较小xml文件。

一切顺利。

我使用以下Ant xml在所有这些XML文件上运行XSLT任务:

    <target name="xsltransform" depends="split" description="Transform XML to SQL...">
            <xslt basedir="${import.dir}/" 
                  destdir="${import.dir}/sql/"
                  style="${xsl.filename}" force="true">
                    <mapper type="glob" from="*.xml" to="*.sql" />
                    <factory name="net.sf.saxon.TransformerFactoryImpl"/>
            </xslt>
    </target>

问题是,一旦它从第一个XML文件开始,我看到linux top 中的'RES'内存随着每个下一个XML文件的增长而增长。由于它正在处理多个(不相关的)xml文件,我怀疑它会在每个xml文件的转换之间释放内存。嗯,它没有...在两百个4MB xml文件之后,java抛出一个内存不足的异常:

BUILD FAILED
/var/lib/hudson/jobs/EPDB_Rebuild_Monthly/workspace/trunk/buildfiles/buildMonthly.xml:67: java.lang.OutOfMemoryError: Java heap space
at net.sf.saxon.tinytree.TinyTree.ensureNodeCapacity(Unknown Source)
at net.sf.saxon.tinytree.TinyTree.addNode(Unknown Source)
at net.sf.saxon.tinytree.TinyBuilder.startElement(Unknown Source)
at net.sf.saxon.event.Stripper.startElement(Unknown Source)
at net.sf.saxon.event.ReceivingContentHandler.startElement(Unknown Source)
at org.apache.xerces.parsers.AbstractSAXParser.startElement(Unknown Source)
at org.apache.xerces.impl.XMLNSDocumentScannerImpl.scanStartElement(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl$FragmentContentDispatcher.dispatch(Unknown Source)
at org.apache.xerces.impl.XMLDocumentFragmentScannerImpl.scanDocument(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XML11Configuration.parse(Unknown Source)
at org.apache.xerces.parsers.XMLParser.parse(Unknown Source)
at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
at org.apache.xerces.jaxp.SAXParserImpl$JAXPSAXParser.parse(Unknown Source)
at net.sf.saxon.event.Sender.sendSAXSource(Unknown Source)
at net.sf.saxon.event.Sender.send(Unknown Source)
at net.sf.saxon.event.Sender.send(Unknown Source)
at net.sf.saxon.Controller.transform(Unknown Source)
at org.apache.tools.ant.taskdefs.optional.TraXLiaison.transform(TraXLiaison.java:194)
at org.apache.tools.ant.taskdefs.XSLTProcess.process(XSLTProcess.java:812)
at org.apache.tools.ant.taskdefs.XSLTProcess.execute(XSLTProcess.java:408)
at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:291)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:106)
at org.apache.tools.ant.Task.perform(Task.java:348)
at org.apache.tools.ant.Target.execute(Target.java:390)
at org.apache.tools.ant.Target.performTasks(Target.java:411)
at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1360)
at org.apache.tools.ant.Project.executeTarget(Project.java:1329)

我能做些什么来阻止XSLT任务占用我的所有记忆? 或者我应该重新考虑我的做法?

1 个答案:

答案 0 :(得分:4)

我们都同意它应该放弃内存,但由于它没有,你可以尝试分解xslt任务以分离调用。例如,使用Ant Contrib's for task

<for param="file">
    <fileset dir="${import.dir}"/>
    <sequential>
        <xslt in="@{file}"
              destdir="${import.dir}/sql/"
              style="${xsl.filename}" force="true">
                <mapper type="glob" from="*.xml" to="*.sql" />
                <factory name="net.sf.saxon.TransformerFactoryImpl"/>
        </xslt>
    </sequential>
</for>

如果这不起作用,那么既然你使用的是Saxon,那么你可以在一个分叉的JVM中calling Saxon's java classes directly。如,

<java classname="net.sf.saxon.Transform" failonerror="true" fork="true">
                <arg value="-s:${import.dir}" />
                <arg value="-xsl:${xsl.filename}" />
                <arg value="-o:${import.dir}/sql" />
</java>

或者你可以尝试两种

<for param="file">
    <fileset dir="${import.dir}"/>
    <sequential>
        <basename property="@{file}.base" file="@{file}" suffix="xml"/>
        <java classname="net.sf.saxon.Transform" failonerror="true" fork="true">
                <arg value="-s:@{file}" />
                <arg value="-xsl:${xsl.filename}" />
                <arg value="-o:${import.dir}/sql/${@{file}.base}.sql" />
        </java>
    </sequential>
</for>

对于奖励积分,你可以尝试通过并行来加快速度。

<for param="file">
    <fileset dir="${import.dir}"/>
    <parallel>
        <basename property="@{file}.base" file="@{file}" suffix="xml"/>
        <java classname="net.sf.saxon.Transform" failonerror="true" fork="true">
                <arg value="-s:@{file}" />
                <arg value="-xsl:${xsl.filename}" />
                <arg value="-o:${import.dir}/sql/${@{file}.base}.sql" />
        </java>
    </parallel>
</for>