java.lang.OutOfMemoryError при преобразовании XML в огромном каталоге

Я хочу преобразовать файлы XML с помощью XSLT2 в огромном каталоге с множеством уровней. Есть более 1 миллиона файлов, каждый файл от 4 до 10 КБ. Через некоторое время я всегда получаю java.lang.OutOfMemoryError: пространство кучи Java.

Моя команда: java -Xmx3072M -XX: + UseConcMarkSweepGC -XX: + CMSClassUnloadingEna bled -XX: MaxPermSize = 512M ...

Добавление дополнительной памяти к -Xmx - не лучшее решение.

Вот мои коды:

for (File file : dir.listFiles()) {
    if (file.isDirectory()) {
        pushDocuments(file);
    } else {
        indexFiles.index(file);
    }
}

public void index(File file) {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    try {
        xslTransformer.xslTransform(outputStream, file);
        outputStream.flush();
        outputStream.close();
    } catch (IOException e) {
        System.err.println(e.toString());
    }
}

XSLT преобразование net.sf.saxon.s9api

public void xslTransform(ByteArrayOutputStream outputStream, File xmlFile) {
    try {
        XdmNode source = proc.newDocumentBuilder().build(new StreamSource(xmlFile));
        Serializer out = proc.newSerializer();
        out.setOutputStream(outputStream);
        transformer.setInitialContextNode(source);
        transformer.setDestination(out);
        transformer.transform();

        out.close();
    } catch (SaxonApiException e) {
        System.err.println(e.toString());
    }
}

person wonder garance    schedule 04.11.2013    source источник


Ответы (4)


Моя обычная рекомендация с интерфейсом Saxon s9api - повторно использовать объект XsltExecutable, но создавать новый XsltTransformer для каждого преобразования. XsltTransformer кэширует прочитанные вами документы на случай, если они снова понадобятся, что вам не нужно в данном случае.

В качестве альтернативы вы можете вызывать xsltTransformer.getUnderlyingController().clearDocumentPool() после каждого преобразования.

(Обратите внимание, что вы можете задавать саксонским вопросам вопросы на saxonica.plan.io, что дает хорошие шансы, что мы [Saxonica] заметим их и ответим на них. Вы также можете задать их здесь и пометить их "saxon", что означает, что мы вероятно, ответите на вопрос в какой-то момент, хотя и не всегда сразу. Если вы спросите на StackOverflow без тегов, относящихся к конкретному продукту, будет совершенно неожиданно, заметит ли кто-нибудь вопрос.)

person Michael Kay    schedule 04.11.2013
comment
Я добавил эту строку кода, чтобы очистить пул документов, это хорошо. Спасибо! Фактически, я подписался на S9APIExamples.java на saxonica.com /documentation/index.html#!using-xsl/embedding/, чтобы написать свою программу, но я не знал, что она очищает пул документов после каждого преобразования. - person wonder garance; 04.11.2013

Я бы проверил, что у вас нет утечки памяти. Количество файлов не имеет значения, поскольку вы обрабатываете только один за раз, и пока вы можете обрабатывать самый большой файл, вы должны иметь возможность обрабатывать их все.

Я предлагаю вам запустить jstat -gc {pid} 10s во время работы программы для поиска утечек памяти. Что вам следует искать, так это размер памяти после полного GC. Если он когда-либо увеличивается, используйте профилировщик памяти VisualVM, чтобы выяснить, почему. Или используйте jmap -histo:live {pid} | head -20 для подсказки.

Если объем памяти не увеличивается, у вас есть файл, который вызывает нехватку памяти. Это потому, что либо а) файл намного больше, чем другие, или использует гораздо больше памяти, б) он вызывает ошибку в библиотеке.

person Peter Lawrey    schedule 04.11.2013
comment
это объяснение звучит интересно и полезно - person Forhad; 04.11.2013

Попробуй это

String[] files = dir.list();
for (String fileName : files) {
    File file = new File(fileName);
    if (file.isDirectory()) {
        pushDocuments(file);
    } else {
        indexFiles.index(file);
    }
}
person Prabhakaran Ramaswamy    schedule 04.11.2013
comment
Не могли бы вы объяснить, как это поможет, поскольку мне кажется, что это то же самое. - person Peter Lawrey; 04.11.2013
comment
@PeterLawrey, моя мысль об объектах File потребует больше памяти. - person Prabhakaran Ramaswamy; 04.11.2013
comment
Хорошая итерация по файловым объектам (если их всего более 1 миллиона), безусловно, потребляет больше памяти, чем просто просмотр строк и создание файловых объектов на ходу (которые затем можно снова собрать в GC). Однако, если это решает проблему, это предположение, которое следует пояснить :) - person Matthias; 04.11.2013
comment
@ByteCode Спасибо. Я изменил ваш код: File file = new File (dir + File.separator + fileName); Но это не решает мою проблему. Правильный ответ Майкла Кея - person wonder garance; 04.11.2013

У меня была аналогичная проблема, которая возникла из пакета javax.xml.transform, который использовал ThreadLocalMap для кэширования фрагментов XML, которые были прочитаны во время XSLT. Мне пришлось передать XSLT в его собственный поток, чтобы ThreadLocalMap очищалась, когда новый поток умер - это освободило память. См. Здесь: https://www.ahoi-it.de/ahoi/news/java-xslt-memory-leak/1446

person Alexander    schedule 13.11.2013