4

我有一个大 (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顶部的“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 任务占用我所有的内存吗?还是我应该重新考虑我的方法?

4

1 回答 1

3

We are all going to agree that it should be letting go of the memory, but since it doesn't, you can try breaking up the xslt task in to seperate calls. e.g., using 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>

If that doesn't do the trick, then since you are using Saxon, you can calling Saxon's java classes directly in a forked JVM. e.g.,

<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>

or you can try both

<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>

and for bonus points you could try to speed things up a bit by doing it in parallel.

<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>
于 2011-06-01T22:41:16.400 回答