Задача Ant XSLT с набором файлов, в котором заканчивается память/не освобождается память

У меня есть большой (1,9 ГБ) файл XML, в котором есть данные, которые я хочу вставлять в базу данных MySQL каждый месяц. Я сделал сценарий Ant для этого.

Задача Ant XSLT не может обработать один такой большой файл, поэтому у меня есть задача, которая использует xml_split (из xml-twig-tools) для разделения файла xml размером 1,9 ГБ на более мелкие файлы xml размером примерно 4 МБ.

Все идет хорошо.

Я использую следующий Ant xml для запуска XSLT-задачи для всех этих XML-файлов:

    <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-файле, я вижу, что память «RES» в linux top растет с каждым следующим XML-файлом. Поскольку он обрабатывает несколько (несвязанных) файлов xml, я подозреваю, что он освободит память между переводом каждого файла xml. Ну, это не так... после двухсот 4-мегабайтных 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 не занимала всю мою память? Или мне стоит пересмотреть свой подход?


person Symen Timmermans    schedule 27.04.2011    source источник
comment
Как и во всех OutOfMemoryError: heap space проблемах, что-то слишком долго удерживает объекты. Это может быть Saxon (маловероятно), или это может быть ваша специальная задача. Способ диагностики состоит в том, чтобы передать -XX:+HeapDumpOnOutOfMemoryError JVM и посмотреть, какие объекты занимают все пространство. Используйте ANT_OPTS для передачи этого значения.   -  person Anon    schedule 03.05.2011
comment
можете ли вы перебирать вызовы муравья вместо того, чтобы одна задача муравья перебирала ваши файлы?   -  person collapsar    schedule 17.05.2011


Ответы (1)


Мы все согласимся с тем, что это должно освобождать память, но поскольку это не так, вы можете попробовать разбить задачу xslt на отдельные вызовы. например, с помощью задачи for Ant Contrib

<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, вы можете вызвать Java-классы Saxon непосредственно в разветвленной JVM. например.,

<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>
person Tom Howard    schedule 01.06.2011
comment
Все это золото. Одна вещь, которую забыли в примерах, это закрывающий тег / для ‹fileset›. - person ; 19.08.2011
comment
Спасибо @GJTorikain. Теперь это исправлено. - person Tom Howard; 21.08.2011
comment
В итоге я использовал решение, очень похожее на ваше. Спасибо! - person Symen Timmermans; 11.06.2012